GithubHelp home page GithubHelp logo

blog's Introduction

npm version Build Status devDependency Status NPM downloads

中文文档

Introduction

tinper-neoui is based on the UI design language IUAP design to achieve the enterprise-class front-end framework. It can help users to quickly build a standard consistent front page and improve the development efficiency

Features

  • Rich components

  • Download resources according to your needs

  • Responsive layout

  • Adapter mainstream browser (IE8+ 、firefox、Chrome、Safari)

Quickstart

Get neoui

  • npm
npm install tinper-neoui
  • cdn
css Path: //design.yonyoucloud.com/static/neoui/latest/css/neoui.css
JS Path: //design.yonyoucloud.com/static/neoui/latest/js/neoui.js

Introducing neoui

  • ES6
import { neoui } from "tinper-neoui"
  • HTML
css:
	 <link href="//design.yonyoucloud.com/static/neoui/latest/css/neoui.css" rel="stylesheet">

JS :
	<script src="//design.yonyoucloud.com/static/jquery/jquery-1.11.2.js"></script>
    <script src="//design.yonyoucloud.com/static/neoui/latest/js/neoui.js"></script>

Note: neoui is dependent on jQuery

Use

<button class="u-button u-button-primary">Hello World!</button>

Read the Develop documentation for information on the framework contents, templates and examples, and more.

Contributing

Feedback

If you encounter any problems , submit issues,or pull request。

PR code

Develop

Developers can participate in the development of neoui, but also can be based on neoui two development

tinper-neoui use gulp.js and webpack build the project.

clone:

$ git clone [email protected]:iuap-design/tinper-neoui.git

install:

$ npm install

build:

$ npm run product

Licence 版权

MIT

blog's People

Contributors

guoyongfeng avatar huyuee avatar kvkens avatar lucian55 avatar onvno avatar wangbow avatar zhaoyuchief avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

blog's Issues

FED团队Git 使用规范流程以及支管理策略

FED团队Git 使用规范流程以及支管理策略

1. 前言

团队开发中,遵循一个合理、清晰的Git使用流程,是非常重要的。
否则,每个人都不合理使用分支,每个人提交一堆杂乱无章的commit,每个人都在随意在主干分支修改,项目很快就会变得难以协调和维护。

2. git基本使用

每次开发一个新功能或是修复一个bug都需要新开一个分支进行管理。

新建分支

$ git checkout develop 
$ git pull origin develop
$ git checkout -b feature-3.0.6-style

修改提交

$ git status
$ git add -A
$ git commit -m "message"

准备合并

$ git checkout develop
$ git fetch origin
$ git rebase origin/develop

分支合并

$ git merge develop feature-3.0.6-style

3. 分支说明

  • 我们的每个人开发仓库都会有master develop release 三大主干分支,develop为开发主干分支,release作为线上运行分支。
  • 每个人开发请基于develop分支单独创建分支完成工作任务,完成后将分支合并到develop分支,在develop测试完成准备上线时,将develop分支更新到releas分支进行部署。
  • 紧急修复生产环境BUG请基于release分支创建hotfix分支进行任务修改。

4.分支命名规则

日常开发主要有以下三种分支:

  • 功能(feature)分支
  • 预发布(release)分支
  • 修补bug(fixbug)分支

这三种分支对应到日常开发的命名规则是:

  • feature-3.0.6-style
  • release-3.0.6-style
  • fixbug-3.0.6-style

其中,第一部分标识分支类型,第二部分标识版本,第三部分标识具体任务。

如何开发前端开源项目

如何开发前端开源项目

作为前端工程师的你,或是接触过前端项目的你,肯定用过或是听过很多前端相关的开源项目。那么,如何做一个开源项目呢?一个开源项目都有哪些注意点?如何参与到开源社区一起贡献代码?今天我们一起来聊聊这个话题。

1. 平台选择

Github是全球最大的社交编程及代码托管网站:https://github.com

  • 人气高,交流方便,聚集了志同道合的人
  • 完善的机制:issue,开源分享,代码托管,wiki,release功能,ci,文档机制等

2. 基础知识概览

Git

git 分布式版本控制系统

掌握基本的Git使用,建议多使用命令行的方式,会更清晰简单。

echo "# git-github" >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin [email protected]:GuoYongfeng/git-github.git
git push -u origin master

markdown

markdown 基本语法

包管理工具

  • npm & nodejs 基础
  • bower

版本号

目前大多产品或开源项目采用类似1.2.3的三位数进行版本号管理。版本号修改规则如下:

  • **主版本号:**当功能模块有较大的变动,比如增加模块或是整体架构发生变化。此版本 号由项目决定是否修改。
  • **次版本号:**相对于主版本号而言,次版本号的升级对应的只是局部的变动,但该局部 的变动造成程序和以前版本不能兼容,或者对该程序以前的协作关系产生 了破坏,或者 是功能上有大的改进或增强。此版本号由项目决定是否修 改。
  • **修订版本号:**一般是Bug 的修复或是一些小的变动或是一些功能的扩充,要经常发布 修订版,修复一个严重 Bug 即可发布一个修订版。此版本号由项目经理 决定是否修改。

3. 快速开始

Git和Github快速入门指南

在Github注册帐户,建立仓库,上传代码。即使你是小白也没有关系,相信看完这篇Git和Github快速入门指南,你也能轻松开始。

项目结构

为大家准备了一个前端开源项目推荐目录结构

.babelrc        # babel 配置文件
.eslintrc       # eslint 代码规则检查配置文件
.gitignore      # git 忽略提交文件配置
.travis.yml     # ci 持续集成构建配置
CHANGELOG.md    # 项目更改日志记录
conf/           # 整体配置文件,构建相关
CONTRIBUTING.md # 如何参与贡献代码
dist/           # 构建产出资源
docs/           # 项目文档
LICENSE         # 版权说明
node_modules/   # node 安装包下载目录
package.json    # npm 包管理配置
README.md       # 项目整体说明
src/            # 开发源代码
test/           # 测试代码

4. 关于文档

文档是你的潜在用户了解项目的第一道窗口,文档书写的好坏直接决定了项目的易用性和用户粘性。

README

我们首先要提的是 README 文档,这是每个开源项目所必须的,README 文档会默认展示在项目首页,可以算作是整个项目的门面。一个靠谱的 README 应该包含以下几部分:

  • 言简意赅的项目介绍:你的项目解决了什么核心问题,有哪些令人心动的特性。
  • 简单的安装和使用指导:用户如何安装你的项目,又如何使用在自己的项目中,你应该想办法让这部分尽量简单,降低接受成本。
  • API 和配置说明:也许你的项目十分强大,支持很多特性,你需要告诉用户如何通过配置和 API 来完成这些事情,在这中间,也许你可以插入一些简单的 demo,帮助用户理解,这部分的好坏直接决定了项目的易用性。

随着你的项目日趋复杂,也许 README 的篇幅已经不能够承载你的 API 说明,这时在项目中单独建立一个 doc 文件夹用来存放文档,也是很多人的选择。

  • 如何贡献:开源的一个很重要的目的是为了让社区中的更多人来帮助你完善你的创意,你需要告诉他们你有哪些部分是他们可以帮的上忙的,你是如何对待 issues 和 pull requests( 后文称 pr) 的。
  • 如何与作者取得联系:有些合作和洽谈可能无法承载在 issue 上,告诉用户除了 issues,还有什么途径可以与作者取得联系。
  • 开源许可协议:既然是一个开源项目,那么开源许可协议必然是不可少的,你的开源许可是 MIT、BSD 还是 ISC、Apache?当然你也需要了解这些开源许可的意义,这里推荐一篇知乎问答。

CONTRIBUTING

上面我们也提到了如何贡献的问题,当贡献需要了解的东西越来越多的时候,我们也习惯于把它单独抽成一份 CONTRIBUTING.md。在贡献文档中应该详细地告诉贡献者,哪些功能点是需要贡献者参与的,如何提 issue 以及 pr。

CHANGELOG

无论什么项目,都是持续迭代开发的。每个版本之间,都会有很多或大或小的改动。通过CHANGELOG.md文档来记录项目每个大小版本之间的改动,方便开发者和用户进行沟通和使用。

LICENSE

除了在 README 中提到遵循的开源协议外,一个靠谱的开源项目还会将该开源协议的内容文档放在自己的项目中。

另外,选择一个合适的开源协议也是很重要的。开源协议比较多,下面聊几个常用的开源协议:

  • **BSD开源协议:**BSD开源协议是一个给于使用者很大自由的协议。基本上使用者可以”为所欲为”,可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有软件再发布。
  • **Apache Licence 2.0:**Apache Licence是著名的非盈利开源组织Apache采用的协议。该协议和BSD类似,同样鼓励代码共享和尊重原作者的著作权,同样允许代码修改,再发布(作为开源或商业软件)。
  • **MIT:**MIT是和BSD一样宽范的许可协议,作者只想保留版权,而无任何其他了限制。也就是说,你必须在你的发行版里包含原许可协议的声明,无论你是以二进制发布的还是以源代码发布的。

更多关于开源协议的内容可以直接查看五种开源协议比较

5. 代码风格

关于代码风格,每个人可能都会有自己的偏好,这些偏好中有好的,也有坏的,一些不好的代码风格会让其他开发者感到不快,减少了大家对于代码的关注,好在强大的开源社区中同样也有人开源了代码风格规范,这些代码规范都经过了激烈的讨论和充分的修改,为大多数人所认可,需要注意的是代码风格并不只是缩进、空格这一类的事情,它更多地是一种代码习惯,比如何时使用 const,何时使用 let,是否有声明但未使用的变量等等,这些习惯并不影响代码的功能,却可以很大程度上决定代码的可维护性、易读性和降低犯错的机会,同时也让其他开发者对你的项目刮目相看。

代码风格推荐

Airbnb

https://github.com/airbnb/javascript

Airbnb 的 js 规范应该是目前受认可度最高的代码规范之一,目前在 Github 上已经累加了 36700+ 颗星,包含的领域非常广泛,包括线下时髦的 ES6 和 React JSX 等等,笔者推荐。

idiomatic.js

https://github.com/rwaldron/idiomatic.js/

这是一份有着很长历史的,由一群经验丰富的 js 工程师维护的代码风格规范,同时也十分通俗易懂,另外他也有简体中文的版本。

jQuery

https://contribute.jquery.org/style-guide/js/

jQuery 所倡导和使用的代码规范,大量的 jQuery 组件根据这一规范书写,如果你读过 jQuery 的源码,你喜欢他的风格,或者你正在开发一款 jQuery 的插件,那这也是一个不错的选择。

Google

https://google.github.io/styleguide/javascriptguide.xml

谷歌倡导的代码风格,自 2012 年推出以后已经有很多谷歌的项目遵循这份规范进行编码,喜欢谷歌风格的朋友可以使用。

LINT

看到这里有人会有疑问,规范虽然好,可是条目太多了,我虽然可以学习,但是全都记住难度太高了。不用担心,你的痛点也是大家的痛点,早已经有工具帮助我们来解决这一问题,而且更棒的是他可以无缝地与你的 IDE 相结合。

在这里我们要推荐 ESLint 这款工具。在不久之前,你还有另一个选择 JSCS,但在最近,JSCS 团队选择与 ESLint 团队进行合并,专注于一款产品 ESLint 的开发,两大大牛团队的合体想必会带给 ESLint 更为强大的生命。

ESlint 提供了非常丰富的 IDE 集成工具,目前已经支持 Sublime Text 3, Vim, Emacs, Eclipse, TextMate 2, Atom, IDEA 和 Visual Studio Code。具体的插件工具请移步 ESlint 的集成文档。下面我们以 sublime 为例,简单讲一下如何使用这些插件:

  • 首先全局安装 ESLint:
npm install -g eslint
  • 接着通过 Package Control,安装 SublimeLinter 和 SublimeLinter-contrib-eslint。
  • 初始化 eslint 配置
eslint --init

这里会有一些人机交互,来选择 eslint 的风格,之后 eslint 就会在你的项目下添加对应的依赖,重启 sublime,就可以看到效果了。有时仅通过编辑器中给出的提示文案,无法帮你准确理解含义,这时我们还可以借助 eslint 的站点:http://eslint.org/docs/rules/ ,这里有更详细的讲解,你还可以直接搜索对应的规则。

6. 测试

靠人工来保证项目的维护总是不出差错是不靠谱的,人总有健忘和糊涂的时候,尤其是当项目越来越复杂时,一个人甚至可能无法全部了解项目的全部逻辑,这时我们就要依靠测试来保证项目的可靠性,这里的测试包括但不限于,单元功能测试,UI 测试,兼容性测试等等。

测试体系

一个测试体系大体应该包含以下三部分,这三者功能上互相独立,但合作完成一次测试:

  • 测试运行器(Test runner):主要负责测试的自动化,主要负责调用测试框架和测试代码,自动打开浏览器或者 js 环境,执行测试。
  • 测试框架(Testing Framework):测试框架规定了测试风格和测试的生命周期(lifeCircle hook)。
  • 断言库(Assertion library):每个测试单元是通过一句或者一组断言来组成的,每一句断言声明了对一种功能的描述。例如 expect(window.r).to.be(undefined);。

Test runner

一个优秀的 runner 可以做很多事情,我们以 Google Angular 团队推出的 karma 为例,你可以指定在什么浏览器中,使用什么测试框架,进一步地完成测试框架的配置,以及其他非常多的定制。他的安装和初始化也非常简单:

# Install Karma:
$ npm install karma --save-dev

# Install plugins that your project needs:
$ npm install karma-jasmine karma-chrome-launcher --save-dev

初始化配置文件

$ karma init my.conf.js

Which testing framework do you want to use?
Press tab to list possible options. Enter to move to the next question.
> jasmine

...

Do you want Karma to watch all the files and run the tests on change?
Press tab to list possible options.
> yes

Config file generated at "/Users/vojta/Code/karma/my.conf.js".

然后就可以运行了

# Run Karma:
$ ./node_modules/karma/bin/karma start

当然这只是最初始化的配置,我们还没有写真正的测试,所以这里只是一个空架子。

测试框架

测试框架有很多:

  • mocha
  • jasmine
  • qunit
  • Jest

对于一般的开发者来说,他们都能够满足日常的测试需求,所以主要看你的使用习惯,这里以 mocha 为例来给大家一个大概的印象。

首先安装 mocha

$ npm install -g mocha
$ mkdir test
$ $EDITOR test/test.js

接下来写你的 test

var assert = require('chai').assert;
describe('Array', function() {
  describe('#indexOf()', function () {
    it('should return -1 when the value is not present', function () {
      assert.equal(-1, [1,2,3].indexOf(5));
      assert.equal(-1, [1,2,3].indexOf(0));
    });
  });
});

运行 test,查看结果

mocha test/test.js

当然上面这个测试只能测试 node 下的 js 代码,如果你的代码是前端项目,那么就要借助 phantom/browser 等工具了,同时操作这么多东西很麻烦,这时 karma 的作用的就体现出来了,本文只是大体的介绍,因此不再铺开去讲,详细请查看 karma 的官方文档。

断言库

断言库也有很多的选择,其中比较有名气的有:

  • expect.js
  • should
  • chai

其中,chai 同时支持 BDD 和 TDD 两种测试模式,而 expect.js 具有 IE6+ 级别的浏览器兼容性。

测试模式

有人会问 BDD 和 TDD 都代表了什么?

  • TDD

TDD(Test-driven development),即 测试驱动开发。即先根据需求写测试,然后再写代码,使代码逐渐符合测试要求,不符合时重构代码满足。这种开发模式适合一些需求非常清晰明确,且不再变更的情况,在一般的开发中,我们还是 BDD 的模式使用的比较多。chai 提供的 assert 部分是经典的 TDD 风格,大致如下

var assert = require('chai').assert
  , foo = 'bar'
  , beverages = { tea: [ 'chai', 'matcha', 'oolong' ] };

assert.typeOf(foo, 'string'); // without optional message
assert.typeOf(foo, 'string', 'foo is a string'); // with optional message
assert.equal(foo, 'bar', 'foo equal `bar`');
assert.lengthOf(foo, 3, 'foo`s value has a length of 3');
assert.lengthOf(beverages.tea, 3, 'beverages has 3 types of tea');
  • BDD

BDD(Behavior-Driven development),即 行为驱动开发。

通常 BD测试提供了几个方法:

  • describe()
  • it()
  • before()
  • after()
  • beforeEach()
  • afterEach()

通过这些方法描述一种行为,当测试的表现满足行为的预期时,即表示测试通过。

7. 持续集成

持续集成,continuous integration (简称 ci),指的是频繁地(一天多次)将代码集成到主干。

为了保证这种快速迭代的开发方式不出差错,采取的核心措施:代码集成到主干之前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。这种行为虽然不能消除 bug,但有效地帮助我们即时发现错误并改正。关于持续集成的详细流程,大家参考阮老师的 持续集成是什么?。本文则重点介绍已经介入 github,可以帮助到我们的继承工具。

接入到 github 的所有 CI 可以在 https://github.com/integrations 中查看。

CI: Travis CI/ CircleCI

两者都是接入 Github 的持续集成工具,其核心是通过一个脚本,在代码 commit 的时候自动运行继承脚本,完成测试、构建等任务。以 Travis CI 为例:

  • 首先在 github 的 Travis CI 页面将 Travis CI 的 hook 加入到你的项目中。
  • 接着在你的项目目录下建立一个 .travis.yml,大致长成这个样子:
language: node_js

sudo: false

notification:
  email:
    - [email protected]

node_js:
- 4.0.0

env:
  matrix:
  - TEST_TYPE=test
  - TEST_TYPE=coverage
  - TEST_TYPE=saucelabs

在不指定 .travis.yml 的情况下,travis 会自动执行 npm install 和 npm test 来进行集成测试。更多的配置可以参考官方的文档。集成通过在两个地方可以有所体现:

  • 一个是 ci 提供的 badge:
  • 一个是在 pr 提交时的自动 comment

跨浏览器集成测试:SAUCELABS & Browser Stack

这两个工具都是提供了多种浏览器环境,包括 pc 端和移动端,然后在多种环境下都是去运行测试脚本,来测试项目的浏览器兼容性。其中 SAUCELABS 对于开源项目完全免费,只需要走他的 Open Source Plan 即可,而 Browser Stack 虽然也提供了 Open Source 的免费计划,但比较麻烦,需要邮件联系,并且在项目 README 中提到其对项目的支持。手动配置这些集成还是比较麻烦的,一般我们都借助 karma 来集成,使用 karma 的 karma-saucelabs-launcher 和 karma-browserstack-launcher。

saucelabs 也提供了很棒的 badge

代码覆盖率集成 Coveralls

代码覆盖率可以在本地测试里集成,同样也可以在 CI 中集成,通过引入 Coveralls,他可以将你持续集成测试中得到的代码覆盖率结果做一个记录和保存,同时及时的反馈给用户。

Coveralls 也有两个地方可以体现:

  • 一个是 Coveralls 提供的 badge:
  • 一个是在 pr 提交时的自动 comment

8. 总结

最后,希望通过上述内容,让大家了解到一个靠谱的前端开源项目应该具备的东西,这个靠谱不仅是为了让用户用的放心,更是为了开发者在开发时心中有谱。

但若要开发一个成功的开源项目,则还需要开发者更多的经营,最重要的,是一份为用户解决痛点的初衷和持之以恒的决心。

scss基础语法与在datatable项目中的运用

什么是sass

SASS是一种CSS的开发工具,提供了许多便利的写法,大大节省了设计者的时间,使得CSS的开发,变得简单和可维护

安装

SASS是Ruby语言写的,必须先安装Ruby,然后再安装SASS。安装完Ruby后,执行

gem install sass

使用

SASS文件就是普通的文本文件,里面可以直接使用CSS语法。下面写法,显示.scss文件转化的css代码

sass test.scss

果要将显示结果保存成文件,后面再跟一个.css文件名。

sass test.scss test.css

基础语法

文件后缀名

sass有两种后缀名文件:一种后缀名为sass,不使用大括号和分号;另一种就是我们这里使用的scss文件,我们项目中采用scss后缀

//文件后缀名为sass的语法
body
  background: #eee
  font-size:12px
p
  background: #0982c1

//文件后缀名为scss的语法  
body {
  background: #eee;
  font-size:12px;
}
p{
  background: #0982c1;
} 

导入important

编译时会将@importscss文件合并进来只生成一个CSS文件

@import "reset.css";
@import "a";

p{
  background: #0982c1;
} 

变量

必须是$开头

普通变量

定义之后可以在全局范围内使用

//sass style
//-------------------------------
$fontSize: 12px;
body{
    font-size:$fontSize;
}

//css style
//-------------------------------
body{
    font-size:12px;
}

默认变量

仅需要在值后面加上!default即可。
默认变量的价值在进行组件化开发的时候会非常有用

//sass style
//-------------------------------
$baseLineHeight:        1.5 !default;
body{
    line-height: $baseLineHeight; 
}

//css style
//-------------------------------
body{
    line-height:1.5;
}

特殊变量

变量作为属性或在某些特殊情况下等则必须要以#{$variables}形式使用

//sass style
//-------------------------------
$borderDirection:       top !default;

//应用于class和属性

.border-#{$borderDirection}{
  border-#{$borderDirection}:1px solid #ccc;
}

//css style
//-------------------------------
.border-top{
  border-top:1px solid #ccc;
}

map

map数据以key和value成对出现,其中value又可以是list。格式为:$map: (key1: value1, key2: value2, key3: value3);。可通过map-get($map,$key)取值。

//sass style
//-------------------------------

$headings:(h1:2em,h2:1.5em,h3:1.2em;

@each $header,$size in $headings{
  #{$header} {
    font-size: $size;
  }
}

//css style
//-------------------------------

h1 {
  font-size: 2em; 
}
h2 {
  font-size: 1.5em; 
}
h3 {
  font-size: 1.2em; 
}

嵌套Nesting

选择器嵌套

使用&表示父元素选择器

//sass style
//-------------------------------
#top_nav{
  line-height: 40px;
  a{
    display: block;
    padding: 0 10px;
    color: #fff;

    &:hover{
      color:#ddd;
    }
  }
}

//css style
//-------------------------------
#top_nav{
  line-height: 40px;
}  
#top_nav a{
  display: block;
  padding: 0 10px;
  color: #fff;
}
#top_nav a:hover{
  color:#ddd;
}

属性嵌套(不常用)

指有些属性拥有同一个开始单词
border-width,border-color

//sass style
//-------------------------------
.fakeshadow {
  border:{
    style:solid;
    left:{
      width:4px;
      color:#888;   
    }
    right:{
      width:2px;
      color:#ccc;
    }
  }


}
//css style
//-------------------------------
.fakeshadow{
  border-style:solid;
  border-left-width:4px;
  border-left-color:#888;
  border-right-width:2px;
  border-right-color:#ccc;
}

@at-root

普通跳出嵌套

//sass style
//-------------------------------
//没有跳出
.parent-1 {
  color:#f00;
  .child {
    width:100px;
  }
}

//单个选择器跳出
.parent-2 {
  color:#f00;
  @at-root .child {
    width:200px;
  }
}

//多个选择器跳出
.parent-3 {
  background:#f00;
  @at-root {
    .child1 {
      width:300px;
    }
    .child2 {
      width:400px;
    }
  }
}

//css style
//-------------------------------
.parent-1 {
  color: #f00;
}
.parent-1 .child {
  width: 100px;
}
.parent-2 {
  color: #f00;
}
.child {
  width: 200px;
}
.parent-3 {
  background: #f00;
}
.child1 {
  width: 300px;
}
.child2 {
  width: 400px;
}

@at-root与&配合使用

//sass style
//-------------------------------
.child{
    @at-root .parent &{
        color:#f00;
    }
}

//css style
//-------------------------------
.parent .child {
  color: #f00;
}

混合mixin

@mixin声明混合

无参数mixin

//sass style
//-------------------------------
@mixin center-block{
  margin-left:auto;   
}
.demo{
  @include center-block;
}
//css style
//-------------------------------
.demo{
  margin-left:auto;
}

有参数mixin

参数名以$符号开始,`也可设置默认参数

//sass style
//-------------------------------
@mixin opacity($opacity:50){
  opacity:$opacity / 100;
  filter: alpha(opacity=$opacity);  
}
//css style
//-------------------------------
.opacity{
  @include opacity; //参数使用默认值
}
.opacity-80{
  @include opacity(80); //传递参数
}

继承extend

//sass style
//-------------------------------
h1{
  border: 4px solid #ff9aa9;
}
.speaker{
  @extend h1;
  border-width: 2px;
}
//css style
//-------------------------------
h1,.speaker{
  border: 4px solid #ff9aa9;
}
.speaker{
  border-width: 2px;
}

占位选择器%

这种选择器的优势:如果不调用则不会有任何多余的css文件,避免了以前在一些基础的文件中预定义了很多基础的样式。
占位选择器以%标识定义,通过@extend调用

//sass style
//-------------------------------
%ir{
  color: transparent;
  text-shadow: none;
  background-color: transparent;
  border: 0;
}
#header{
  h1{
    @extend %ir;
    width:300px;
  }
}
//css style
//-------------------------------
#header h1{
  color: transparent;
  text-shadow: none;
  background-color: transparent;
  border: 0;
  width:300px;
}

函数

常用的为颜色函数,lightendarken为最。lighten($color,$amount)darken($color,$amount)

//sass style
//-------------------------------
$baseFontSize:      10px !default;
$gray:              #ccc !defualt; 
// pixels to rems 
@function pxToRem($px) {
  @return $px / $baseFontSize * 1rem;
}

body{
  font-size:$baseFontSize;
  color:lighten($gray,10%);
}
.test{
  font-size:pxToRem(16px);
  color:darken($gray,10%);
}

//css style
//-------------------------------
body{
  font-size:10px;
  color:#E6E6E6;
}
.test{
  font-size:1.6rem;
  color:#B3B3B3;
}

条件判断及循环

@if判断

//sass style
//-------------------------------
$lte7: true;
$type: monster;
.ib{
    display:inline-block;
    @if $lte7 {
        *display:inline;
        *zoom:1;
    }
}

//css style
//-------------------------------
.ib{
    display:inline-block;
    *display:inline;
    *zoom:1;
}

三目判断

/if(true, 1px, 2px) => 1px
if(false, 1px, 2px) => 2px

for循环

//sass style
//-------------------------------
@for $i from 1 through 3 {
  .item-#{$i} { width: 2em * $i; }
}

//css style
//-------------------------------
.item-1 {
  width: 2em; 
}
.item-2 {
  width: 4em; 
}
.item-3 {
  width: 6em; 
}

datatable项目sass应用

sass源文件目录datatable\src\ui,项目中scss模块化管理,根据scss功能进行文件化管理

  • utility.scss 初始样式
  • global.scss 通用全局样式
  • minxins.scss 所有的混合放在这里
  • variables.scss 变量定义集合
  • color.scss 关于色值得变量定义(需配合palette.scss)
  • themeColor.scss 定义默认主题色
  • u.scss import所有scss
  • 功能页面scss 比如tooltip.scss

Js中ScrollTop、ScrollHeight、ClientHeight、OffsetHeight等整理

一直对ScrollTop、ScrollHeight、ClientHeight、OffsetHeight这些内容傻傻分不清楚,今天整体下。

scrollHeight

scrollHeight含有scroll当然这个高度与滚动相关。

  • 读写:只读
  • 描述:包括overflow样式属性导致的视图中不可见内容,没有垂直滚动条的情况下,scrollHeight值与元素视图填充所有内容所需要的最小值clientHeight相同。包括元素的padding,但不包括元素的margin.
    scrollHeight
  • 拓展:判定元素是否滚动到底
     如果元素滚动到底,下面等式返回true,没有则返回false.
     element.scrollHeight - element.scrollTop === element.clientHeight

scrollTop

  • 读写:可读可写
  • 描述:这个Element.scrollTop 属性可以设置或者获取一个元素距离他容器顶部的像素距离。一个元素的 scrollTop 是可以去计算出这个元素距离它容器顶部的可见高度。当一个元素的容器没有产生垂直方向的滚动条,那它的 scrollTop 的值默认为0.
    scrollHeight
  • 注意事项:scrollTop可以被设置任何的整数, 但以下情况会报错:
    • 如果一个元素不能被滚动 (e.g. 它没有益处容器或者 这个元素是不可滚动的), scrollTop被设置为0.
    • 设置scrollTop的值小于0,scrollTop 被设为0
    • 如果设置了超出这个容器可滚动的值, scrollTop 会被设为最大值.

    clientHeight

  • 描述:返回元素内部的高度(单位像素),包含内边距,但不包括水平滚动条、边框和外边距。
    clientHeight 可以通过 CSS height + CSS padding - 水平滚动条高度 (如果存在)来计算

clientTop

  • 读写:只读
  • 描述:一个元素顶部边框的宽度(以像素表示)。不包括顶部外边距或内边距。可以理解成定义border的高度

offsetHeight

  • 读写:只读
  • 描述:它返回该元素的像素高度,高度包含该元素的垂直内边距和边框,且是boder一个整数。
    通常,元素的offsetHeight是一种衡量标准,包括元素的边框、垂直内边距和元素的水平滚动条(如果存在且渲染的画)和元素的CSS高度。
    scrollHeight

offsetTop

  • 读写:只读
  • 描述:它返回当前元素相对于其 offsetParent 元素的顶部的距离(子边框外侧到父边框的内存的距离)

Browsersync 调试手机端样式

安装

在终端里输入:
$ npm install browser-sync gulp --save-dev
全局安装
$ npm install -g browser-sync

启动

实时刷新

如果您想要监听.css文件, 您需要使用服务器模式。 BrowserSync 将启动一个小型服务器,并提供一个URL来查看您的网站。

// --files 路径是相对于运行该命令的项目(目录)
browser-sync start --server --files "css/*.css"

以上实现了对css的试试刷新

动态网站

如果您已经有其他本地服务器环境PHP或类似的,您需要使用代理模式。 BrowserSync将通过代理URL(localhost:3000)来查看您的网站。

// 主机名可以是ip或域名
browser-sync start --proxy "主机名" "css/*.css"

调试

  • 启动之后手机端访问http://localhost:3000页面

  • pc端访问http://localhost:3001页面

    界面操作简洁易懂。我们着重关注的是Remote Debug这一项,默认它的所有选项都是关闭的。我们开启Remote Debugger (weinre)这一项,然后点击出现的红色字:Access remote debugger (opens in a new tab),就会打开weinre的控制台界面。这个界面上会列出当前连接在BrowserSync上的客户端,我们可以选择一个目标(target)来调试

此调试有些局限,对于js调试还不支持、

FED团队招聘

FED团队招聘

我们是谁

  • 我们是用友网络iUAP前端技术部,这是一支由专业的前端工程师组成的一个FED团队。
  • 我们致力于提升用友网络各个产品线和项目组的研发效率和用户体验。
  • 我们的框架、工具、解决方案推广到了公司内外绝大多数产品线
  • 我们崇尚开源自由,我们追求高效卓越,我们是一群热爱阳光的年轻人。

加入我们一起飞

可以描述我们团队的标签:iuap-design、Nodejs、React、Github、Vim、Koa、Geek、Git、Saas、Knockout、Gulp、Sass、Webpack

我们是技术的弄潮儿,绝不局限于某个产品或技术。我希望你不仅关注前端技术,还要重视全端和全栈的能力。加入我们,成就自己。

我们需要这样的你

我们希望你是开源的爱好者,常年混迹于开源社区,对技术敏感。

  1. 大专以上学历,计算机软件、通讯相关专业本科优先
  2. 至少2年以上前端开发经验,1年以上的html5开发经验
  3. 精通html5/CSS3/Javascript,对于页面效果实现有足够的经验
  4. 有ps切图经验,对视觉和交互有自己的理解。
  5. 使用过jquery、zepto、bootstrap等基础类库和框架,有一定的插件开发能力
  6. 熟悉前后端开发流程,有Nodejs或是java的基本开发经验
  7. 熟悉AMD和commonjs等模块化规范
  8. 熟悉git版本控制软件,会用基本的shell命令
  9. 在github上有过相关开源项目的优先
  10. 有过Webpack、Gulp或者FIS等工具的使用经验者优先
  11. 有React、Angular、Vuejs等类库或框架使用经验者优先
  12. 有较强的自学能力和钻研精神,对技术比较敏感

福利待遇

  • 我们有班车接送,你可以在上下班途中美美的睡一觉;
  • 用友产业园的美景,是其他公司无法比拟的,你可以在园区听到鸟鸣闻到花香
  • 我们的餐厅,不仅仅一个,给你廉价而美味的美食
  • 我们的团队工作氛围轻松,还有漂亮的妹纸哦

联系我们

地址:北京市海淀区北清路68号用友软件园西区

前端模板引擎学习

前端模板引擎学习

之前在练手node时,用过ejs模板,当时也对比过Jade,围观了一些前人在使用上的经验,众说纷纭.

有支持ejs,从语义上前端看着更舒服,如下。

<% if(!loginStatus) { %>
<div class="hero-unit">
    <h1>歡迎來到 Microblog</h1>
    <p>Microblog 是一個基於 Node.js 的微博系统。</p>
    <p>
        <a class="btn btn-primary btn-large" href="/login">登入</a>
        <a class="btn btn-large" href="/reg">立即註冊</a>
    </p>
</div>
<% } else { %>
<form method="post" action="/post" lass="well form-inline center" style="text-align:center;">
    <input type="text" class="span8" name="post">
    <button type="submit" class="btn btn-success"><i class="icon-comment icon-white"></i>發言</button>
</form>

而Jade的支持者,更是直言:**"肯定是Jade。EJS不要说跟Jade比,就是跟传统模板技术,比如Smarty比,也很挫了,比如不支持模板继承。"**Jade的语法结构,不如让我们看看:

doctype html
html(lang="en")
  head
    title= pageTitle
    script(type='text/javascript').
      if (foo) {
         bar(1 + 5)
      }
  body
    h1 Jade - node template engine
    #container.col
      if youAreUsingJade
        p You are amazing
      else
        p Get on it!
      p.
        Jade is a terse and simple
        templating language with a
        strong focus on performance
        and powerful features.

整体来说,即便如此,Jade的2个空格从心理上是接受不了的。现在问题来了,项目有需求:利用模板引擎完成页面内容的复用和嵌套,完成上线的静态页面优化。

那么该是创新的Jade,还是ejs,答案是:都不用。至少目前从需求来看,在部分功能不需求的情况下,前端模板引擎artTemplate能帮助完成。

在介绍之前,目前浅显的认为,模板引擎有以下明显优势:

  • 代码复用
  • 页面组件化

因为目前页面是静态化,所以以自己短浅的眼光看,用真正意义上的.html更能拉近前端和这个引擎世界的距离,毕竟.ejs,.jade都有种拒人千里之外的感觉。

试试artTemplate

官方第一种方法是通过页面加载template.js文件,自定义script type="text/html"来实现。demo如下

<head>
...
<script src="./template.js"></script>
</head>

<body>
<!--待插入的内容-->
<div id = "content"></div>

<!--编写模板-->
<script id="test" type="text/html">
<h1>{{title}}</h1>
<ul>
    {{each list as value i}}
        <li>索引 {{i + 1}} {{value}}</li>
    {{/each}}
</ul>
</script>

<!--渲染模板-->
<script>
var data = {
    title: '标签',
    list: ['文艺', '博客', '摄影', '电影', '民谣', '旅行', '吉他']
};
var html = template('test', data);
document.getElementById('content').innerHTML = html;
</script>
</body>

使用以上确实可实现渲染页面,特性和缺失如下:

  • 实现方式简单
  • 页面分离不够彻底美观
  • 页面渲染在前端执行,影响性能
  • 无法实现{{include}}

前边的一定程度上都可以忍受,但是无法实现{{include}}模板嵌套就没法忍了。

于是有了第二种方法:

针对artTemplate的预编译工具:TmodJS

TmodJS

tmodJS实现了{{include}}的功能,语法也基本与artTemplate一致,所以学习成本来说是极低的。

只需要安装tmodjs命令行工具:

npm install -g tmodjs

index.html页面实际如下:

{{include './header'}}

<div id="container">
    {{include './public/banner'}}

    <!-- 判断主体内容 -->
    {{if position == 'temppage'}}

    {{include './public/tempcont'}}

    {{/if}}

</div>

{{include './footer'}}

引用的公共区域以banner为例,内容如下,可以设置变量

<!-- banner开始 -->

<div class="banner">
    <div class="u-container">
        <div class="banner-content">
            <h1>{{title}}</h1>
            <p class="info">{{description}}</p>
        </div>
    </div>
</div>


<!-- banner结束 -->

通过运行

tmod

会默认将公共文件打包压缩为template.js,页面加载此文件,从而实现{{include}},然后其他执行如第一种方法。

从整体来看,除了实现引用{{include}},tmod依然没有解决我们后端直接输出html页面的需求。

好在我们还有第三种方法:

artTemplate的node版本

好了,支持

  • 支持预编译
  • 直接编译输出html页面
  • 命令简单

安装

npm install art-template

demo:

index.html

<html>
<head>
<meta charset='utf-8'>
<title>模板</title>
</head>

<body>
     <div id=”main”>
        {{include './common/header'}}
          <ul>
               {{each list}}
               <li>编号:{{$value.id}} &nbsp;&nbsp;姓名:{{$value.name}}</a></li>
               {{/each}}
          </ul>
     </div>
</body>
</html>

node中编译,将index.html输出为完整的静态页test.html

var fs = require('fs');
var template = require('art-template');
var datalist = require('./data.json'); // 引入数据

//渲染模板
var html = template('./index', datalist);
fs.writeFileSync('./test.html', html);

data.json内容

{"list": 
    [{"id":1, "name":"张三2"}, {"id":2, "name":"李四"}]
}

最终test.html输出结果

<html>
<head>
<meta charset='utf-8'>
<title>模板</title>
</head>
<body>
     <div id=”main”>
        <!-- 导航开始 -->
        ...
        <!-- 导航结束 -->
          <ul>

               <li>编号:1 &nbsp;&nbsp;姓名:张三2</a></li>

               <li>编号:2 &nbsp;&nbsp;姓名:李四</a></li>

          </ul>
     </div>
</body>
</html>

下边就是用node将整个文件复制编译出了,有时间再写。

2016/6/21 团队工作会议

#2016/6/21 团队工作会议

1.部门工作整体介绍

目前整体划分了八大板块内容:

  1. 开始使用。包括框架说明、基础和进阶的开发指导、前端架构、反馈交流
  2. 设计语言。包括模式和具体设计语言
  3. CSS组件。包括全局样式和30+CSS组件(控件)的开发示例及开发文档
  4. JS 插件。目前内容暂无,规划为展示内部开发的JS插件,以及链接相关的第三方插件
  5. 模板库。前期包括部分领域的典型页面集合以及我们开发的默认页面实例
  6. WebIDE。基于现有的调试页面,展示并增强样式板的效果。后续打造成易用的开发调试工具
  7. Kero。原datable,赋予新的产品名并独立开发,原代码不修改,在官网展示详细的开发和示例文档内容
  8. 定制下载。对框架的主题级定制、对控件和插件的打包定制。

2.我们的代码仓库的介绍

官网

  • iuap-design.github.io官网的代码
  • gitbook-plugin-iuap-design把我们的md文档转换成html的gitbook插件
  • generate-uui完成两件事情,将资源放在dist目录实现CDN分发、聚合其他各个相关仓库代码

框架

  • iuap-design基于设计语言iUAP Design的UI框架
  • datetimepicker日期时间相关的
  • datatable 马上需要改名为kero,kero是一个基于ko的扩展的类库,做为MVVM架构中Model层的增强,主要用于处理企业级复杂数据交互。
  • tree 处理复杂树形数据展现的tree插件
  • grid

生态

  • scaffold 标准化目录结构
    -webpack-dev-boilerplate 基于webpack + react的脚手架
  • react-starter-kit 暂无内容
  • cloud-starter-kit 基于koa+gulp+require的项目脚手架
  • user-group 用户维护仓库

团队

  • blog 团队博客

3.官网和CDN构建的过程

CDN

  • 我们的CDN资源分两个部分,generate-uui仓库的dist目录下的资源,通过在nginx服务配置软链接的方式,配置了CDN资源分发
  • 我们的官网下的所有静态资源都走了CDN加速

官网构建

我们的官网展示很简单,但是资源较多

  • 文档编译,在docs目录下写的文档,会构建到dist/pages目录,通过执行bin/gitbook-build.sh 脚本可以一键式的编译我们docs下的所有文档,在服务器上的钩子hook也是执行的这个脚本实现自动编译的
  • node服务,我们官网整体是使用的koa起的web服务,在nginx服务器上也是一直起了这个服务,所以在调试的时候,执行node server/app.js 即可,另外,我把所以和server相关的东西都放在server目录下
  • static目录下的CDN资源,我们在访问官网的static目录下的资源其实都是直接访问的generate-uui仓库的dist目录下的资源

权限

目前大家都可以往github上的iUAP Design上面自由推送代码,但是服务器上部署的代码真正的生效,目前只有我有权限,而且需要我按固定的格式提交代码才会生效。这是因为我们使用的webhook做的钩子,实现的自动化部署,

4.代码规范

不要再去写面条一样的jquery代码

合理的组织js代码

纯函数式

/**
 * [task description]
 * @param  {[type]}  [description]
 */
function Init(){
    domRneder();
    handleEvent();
}

/**
 * [task description]
 * @param  {[type]}  [description]
 */
function domRneder(){
    console.log('domRneder.....')
}

/**
 * [task description]
 * @param  {[type]}  [description]
 */
function handleEvent(){
    console.log('handleEvent......')
}

单例模式

var Index = {
    /**
     * [task description]
     * @param  {[type]}  [description]
     */
    init: function (){
        this.domRender();
        this.handleEvent();
    },
    /**
     * [task description]
     * @param  {[type]}  [description]
     */
    domRneder: function(){
        console.log('domRender');
    },
    /**
     * [task description]
     * @param  {[type]}  [description]
     */
    handleEvent: function(){
        console.log('handleEvent');
        this.otherFn();
    },
    /**
     * [task description]
     * @param  {[type]}  [description]
     */
    otherFn: function(){
        console.log('todo....')
    }
};

Index.init();

构造函数加原型

  • 3.1第一种
/**
 * [task description]
 * @param  {[type]}  [description]
 */
var Robot = function(){
    this.a = 'a';
    this.b = 'b';
    this.init();
 }

/**
 * [task description]
 * @param  {[type]}  [description]
 */
Robot.prototype.init = function(){
    this.say();
    this.work();
}

/**
 * [task description]
 * @param  {[type]}  [description]
 */
Robot.prototype.say = function(){
    console.log('say...')
}

/**
 * [task description]
 * @param  {[type]}  [description]
 */
Robot.prototype.work = function(){
    console.log('work...')
}

new Robot();
  • 3.2第二种
/**
 * [task description]
 * @param  {[type]}  [description]
 */
function Detail(){
    this.init();
};

/**
 * [task description]
 * @param  {[type]}  [description]
 */
Detail.view = {
    "PRICE_BTN" : $("._price_btn a")
}

/**
 * [task description]
 * @param  {[type]}  [description]
 */
Detail.prototype = {
    init : function(){
        this.initUrl();
    },
    initUrl : function(){
        console.log('handle.....')
    }
}

new Detail();

ES6 Module

class Person {
    constructor(){
        this.a = 'a';
        this.b = 'b'
    }
    say(){
        console.log('say.....')
    }
}

new Person();


代码间隙

// 1
if ( a > 0 ) { }

// 2
var x = 20;

// 3
for ( var i = 0, len = arr.length; i < len; i++ ) {

}

注释

/**
 * [task description]
 * @param  {[type]}  [description]
 */
function Detail(){
    this.init();

    // 变量定义
    var a = 'xx';
};

公共的方法进行抽离

4个空格缩进

// bad
function a ( ) {

    if () { }
  for () {}

        if () { }
}

// good
function a ( ) {

    if () { }
    for () {}
    if () { }
}

命名

  • 名副其实,见名知意
  • 小驼峰
// bad
var x1 = 0, a_77 = b;

// good
var newFeature = [];

断行

// bad
$.a().b({a: b}).c().dd()

// good
$.a()
 .b({a: b})
 .c()

json格式

// bad
var a = {a: 9,c:0,cccc:'9409043'}

// good

var a = {
    a: 9,
    c: 0,
    cccc: '9409043'
}

5.git的使用和提交信息规范

  1. 合理使用分支
  2. 习惯用命令行,图形化工具可以作为辅助
  3. 提交的信息要有意义,说明解决了什么bug,添加了什么feature功能
  4. 习惯用 git status,时刻知道你当前在什么分支,当前的文件处于什么样的状态

【转】同步与异步&阻塞与非阻塞

同步与异步&阻塞与非阻塞

本文转载并整理自haoran_10

一、同步与异步的区别

1、概念介绍

  • 同步:所谓同步是一个服务的完成需要依赖其他服务时,只有等待被依赖的服务完成后,依赖的服务才能算完成,这是一种可靠的服务序列。要么成功都成功,失败都失败,服务的状态可以保持一致。
  • 异步:所谓异步是一个服务的完成需要依赖其他服务时,只通知其他依赖服务开始执行,而不需要等待被依赖的服务完成,此时该服务就算完成了。至于被依赖的服务最终是否真正完成,无法确定,所以它是不可靠的服务序列。

2、消息通知

  • 同步:当一个同步调用发出后,调用者要一直等待返回消息(或者调用结果)通知后,才能进行后续的执行;
  • 异步:当一个异步过程调用发出后,调用者不能立刻得到返回消息(结果)。实际处理这个调用的部件在完成后,通过消息回调来通知调用者是否调用成功。

调用者获取依赖服务异步回调结果一般有两种方式,一种是主动去轮训查询异步回调的结果,一种调用依赖服务时传入一个callback方法或者回调地址,依赖服务完成之后去调用callback通知调用者。一般情况,这两种方式都要支持才是一种良好的异步回调设计方法。

3、场景比喻

小明去买奶茶,可能会有两种方式:

  • 小明点单交钱,然后等着取奶茶;
  • 小明点单交钱,然后奶茶妹给了小明一个小票,等小明的奶茶做好了,再告诉小明来取;

第一种方式就是同步,就等着奶茶妹做好奶茶,奶茶做好之后,小明拿到奶茶才算完成整个任务
第二种方式就是异步,奶茶妹给了小明一个小票,小明就算完成了。至于最后奶茶做好没有,反正奶茶妹会告诉小明的,那是后面的事情了。

4、总结

同步与异步着重点在消息通知的方式,也就是调用结果通知的方式。结合场景就是,拿到奶茶的方式。

二、阻塞与非阻塞的区别

1、概念介绍

  • 阻塞:阻塞调用是指调用结果返回之前,当前线程会被挂起,一直处于等待消息通知,不能够执行其他业务。函数只有在得到结果之后才会返回。

Xml代码

阻塞调用和同步调用不同的。对于同步调用来说,很多时候当前线程可能还是激活的,只是从逻辑上当前函数没有返回而已,此时,这个线程可能也会处理其他的消息。  
还有一点,在这里先扩展下:  
(a) 如果这个线程在等待当前函数返回时,仍在执行其他消息处理,那这种情况就叫做同步非阻塞;  
(b) 如果这个线程在等待当前函数返回时,没有执行其他消息处理,而是处于挂起等待状态,那这种情况就叫做同步阻塞;  
所以同步的实现方式会有两种:同步阻塞、同步非阻塞;同理,异步也会有两种实现:异步阻塞、异步非阻塞;  
对于阻塞调用来说,则当前线程就会被挂起等待当前函数返回;  
  • 非阻塞:非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
    虽然表面上看非阻塞的方式可以明显的提高CPU的利用率,但是也带了另外一种后果就是系统的线程切换增加。增加的CPU执行时间能不能补偿系统的切换成本需要好好评估。

2、场景比喻

还是小明去买奶茶,可能会有两种方式

  • 小明点单交钱,然后等着取奶茶;
    • 小明在等着取奶茶的时候呢,啥都不干,就等着;小明等奶茶的行为就叫做阻塞,小明在等奶茶的时候,阻塞了!而这种方式又叫做同步阻塞。
    • 小明在等着取奶茶的时候呢,翻出手机,一会翻翻微信朋友圈,一会瞅瞅奶茶妹有没有做好奶茶;小明这种行为就叫做非阻塞,因为没有阻塞在等奶茶这件事情上。而这种方式又叫做同步非阻塞。
  • 小明点单交钱,然后奶茶妹给了小明一个小票,等小明的奶茶做好了,再告诉小明来取;
    • 奶茶妹给了小明一个小票,然后小明还是傻等着;小明这种拿到票还傻等着的行为,就叫阻塞。而这种行为,又叫做异步阻塞!这种最傻了。
    • 奶茶妹给了小明一个小票,然后小明翻出手机一直刷朋友圈,过了一会儿奶茶妹说小明你的奶茶好了,过来拿,小明才放下手机去拿奶茶。这种方式就叫做非阻塞,又叫着异步非阻塞!

3、总结

阻塞与非阻塞的着重点在于当前线程等待消息返回的行为。换成场景就是,小明等奶茶的行为。

三、大总结

  • 同步阻塞:小明啥都不干等奶茶。
  • 同步非阻塞:小明一边玩手机一边等奶茶。
  • 异步阻塞:小明拿着小票啥都不干等着奶茶妹告诉他奶茶好了
  • 异步非阻塞:小明拿着小票玩着手机等着奶茶妹告诉他奶茶好了

css3 transition 笔记

transition 属性值

css 动画 transition是过渡的意思,元素从一种样式组件改变到另外一种效果

  • transition-prototype 需要过渡的属性,默认值all。必输
  • transition-during 持续时间,默认值0。 必输
  • transition-timing-function 速度曲线,默认值ease
  • transition-delay 多长时间后执行过渡,默认值0

transition-timing-function

transition 的另外三个值比较明了,唯独这个略显神秘。
它的取值范围如下:

  • linear 匀速 (cubic-bezier(0,0,1,1))
  • ease 匀加速-匀减速 (cubic-bezier(0.25,0.1,0.25,1))
  • ease-in 匀加速 (ubic-bezier(0.42,0,1,1))
  • ease-out 匀减速 (cubic-bezier(0,0,0.58,1))
    -ease-in-out 加速-减速 (cubic-bezier(0.42,0,0.58,1))
  • cubic-bezir(n,n,n,n) n取值去0-1

ease-in-out 与 ease 很类似,只是速度曲线不一样,具体表现在贝塞尔值不同

实例讲解

<style> 
div
{
      width:100px;
      transition:width 2s;
}

div:hover
{
      width:300px;
}
</style>

当鼠标hover到div上 这个div的width花2s时间以ease的曲线延长到300px。

transition 可接受多个属性值

<style> 
div
{
      width:100px;
      height:50px;
      transition:width 2s,height 2s;
}

div:hover
{
      height: 100px;
      width:300px;
}
</style>

transform 属性

<style> 
div
{
     width:100px;
     height:100px;
     transition:width 2s, height 2s,transform 2s;
}

div:hover
{
       width:200px;
       height:200px;
       transform:rotate(180deg);
}
</style>

transform 如width一样是属性,定义元素旋转缩放,移动和倾斜。

transform:transform-function

transform-function 取值参见与 http://www.w3school.com.cn/cssref/pr_transform.asp

因transform 这个神奇的属性 transition可以轻松模拟许多有意思的动画

gitbook使用问题解决方案

gitbook使用问题解决方案

最近使用gitbook的时候,出现两个诡异的问题:

  1. build产出资源不能到达指定的目录
  2. gitbook install 资源的时候出现异常

解决方案

删除当前用户的gitbook

rm -rf ~/.gitbook/**
npm install [email protected]
cd ~/.gitbook && mkdir 2.6.7 && cd 2.6.7
cp -R ~/node_modules/gitbook/** ./

更新gitbook-cli

npm install gitbook-cli -g
# 注意,不能全局安装gitbook,只能全局安装gitbook-cli

重新到book.json目录install

gitbook install
gitbook serve

iUAPDesign 主题色与更换机制

iUAPDesign 主题色与更换机制

iUAP Design将换肤的概念定义为:更换主题配色及基础样式切换。

主题色包含:关键色、辅助色
础样式包含:细颗粒控件的

关键色负责:品牌形象、视觉查分的建立、
辅助色:突出及活跃页面视觉

基于不同企业品牌因素、差异化因素、历史因素等需求的存在,iUAP Design在设计初期即把主题色的更换作为产品特点写入规划。

分析当前的主流做法,基本分为两种解决方案:

1.预制多个样式文件,通过功能按钮或手动改写css文件路径,达到换肤目的。这是最常见的方式,效果也比较明显,但是它有几个缺点。

缺点:

1.1.如果更改一个皮肤的内容,那其他的皮肤文件也要做相应修改(这挺麻烦,不过可以用less管理css解决,所以也不是什么大问题)。

1.2.它是固定的,在使用的时候皮肤文件已经是写好的了,而当我需要动态设置一些属性的时候就无可奈何了。

2.使用less这种工具,在页面加载时动态生成皮肤样式。

但这种方式的缺点却很明显。

缺点:

2.1.效率低,为了一个换肤的功能,却要引一个less工具(压缩后大概126KB),在终端Web是无法忍受的。

2.2.速度慢,因为有一个皮肤文件编译(姑且把它叫做编译)生成的过程,在终端也难以接受(有可能还出现页面皮肤闪),如果更换皮肤又要重新编译。

鉴于上面两种方法的缺点,一种无法动态,另一种效率低。

有没有第三种方法呢?

第三种方法就是:
变化分离
确定页面元素中变与不变的元素。

在iUAP Design中需要明确几个概念,哪些视觉元素是通用的有明确视觉、心理、体验隐喻及涵义的。除此之外,可以抽取出可以明显改变界面主题色彩风格的元素。我们把主题色命名为关键色,关键色通过配置关联相关的页面元素:控件、碎片、组件。

因为iUAP Design是按照原子化设计思路进行的元素解构,所以,我们就根据这些元素分析哪些是换肤所需要的部分,并将他们抽取出来,
下面三个是最为明显的换肤元素及属性:

1.导航栏(页面框架导航NavigationBar、TabBar等及其关联的颜色及字体颜色)

2.主按钮

3.链接色

4.主题色

5.背景色

6.主辅助色

iUAP Design的主题配色更换方法

根据上面的两个总结,我们发现没必要把页面换肤定义成复杂的一件事,不需要用什么工具或切换css文件。只需要获取需要动态设置的属性,然后然后设置到需要换肤的元素就可以了。
但是考虑到用户的特性,iUAP Design的主题配色更换采用两个方法:

1、切换主题样式文件

iUAP Design根据牛顿光谱确选定彩虹7色(红橙黄绿蓝靛紫),结合Material Design色板扩展为预制基础样式配色。用户只需要在页面饮用skins文件下的对应样式css文件即可更换主题配色。

7套预制主题色样式分别为:

indigo(靛蓝)--Creative(创意、直觉力、睿智)
Green(绿色)——Nature(自然、健康、生命、成长)
Orange(橙色)——Healing(宽容、力量)
Red(红色)——Life(激情)
Blue (cyan)(天蓝)——Harmony(和谐)or Freedom(自由)
Yellow(黄色)——Sunlight(阳光,或可以公开的社会氛围)
Purple(紫色)——Spirit(灵魂)

2、更改动态设置属性

通过动态属性的设置可以改变主题配色和基础控件的外观样式(直角、小圆角、圆角)。

iuap design 源码分析系列

iuap design 源码分析系列

iuap design来源

在前端未大爆发时代,各个互联网公司和传统软件公司在技术和人才上基本上都是遵循重后端、轻前端的策略。
用友同样如此,用友的NC产品(大型企业与集团管控产品线)在前端领域分为两大阵营:经典NC和LFW。经典NC的前端就是Swing前端,这个技术在Java教科书上可能早都当做不学习的几个章节了,但在NC,无论是页面的效率还是UE效果,都堪称Swing的巅峰之作;LFW,企业轻量级框架,是一种屏蔽前端技术、只用Java就可以开发出前端页面的技术,它有以下优点:只会Java就可以实现全栈开发、强大的设计器可以快速拖拽式开发、规范化页面不用UE人员参与。快速开发带来了很多效率上的提升,但对于个性化的页面就有些力不从心了。
iuap design是iuap 提出来的新一代的web前端解决方案,包括前端控件库、编程模型、工程化解决方案,具体介绍请参考官网的首页。

系列大纲

  • 双MVVM框架
  • DataTable
  • 封装的ajax:ServerEvent
  • 基础工具方法实现
  • 控件实现机制
  • 控件中的编程技巧
  • 其他
    未完待续,欢迎建议,欢迎指正!

Knockout入门

#1.Knockout如何使用

1. 引入Konckout文件

<script src="http://design.yyuap.com/static/knockout/knockout-3.2.0.debug.js"></script>

2. 在js中定义一个view model并绑定

<script type='text/JavaScript'>
    //定义viewModel
    var viewModel={
        name:'lsz',
        age:'23'
    };
    //开始绑定
    ko.applyBindings(viewModel);
</script>

3. 在knockout中,每个HTML的DOM对象都是通过data-bind属性来绑定数据的。

例如:

<p data-bind="text:name"></p>
<p data-bind="text:age"></p>

说明:ko.applyBindings()两个参数,第一个参数是用于绑定的对象。第二个参数(可选),可以指定使用knockout的DOM元素或者容器。例如 ko.applyBindings(viewModel,document,getElementById('div'))。它现在的功能是只有对id为div的元素和子元素生效
#2.knockout的DOM元素上的属性说明

2.1 text绑定

text绑定到DOM元素上,使得该元素的文本值为你绑定的参数值。
例如:

<span data-bind="text:name"></span>
<script type="text/javascript">
  var viewModel={
     name:'lsz'
  }
  ko.applyBindings(viewModel);
</script>

说明:knockout将参数值设置在指定DOM元素的innerText(IE)或textContent(Firefox)属性上。原来的文本将会被覆盖

2.2 value绑定

value绑定是关联DOM元素的值到view model的属性上。主要是用在表单控件input,select和textarea上。
例如:

<p data-bind="text:name"></p>
<input data-bind="value: name"/>
<script type="text/javascript">
    var viewModel={
       name:ko.observable('lsz')
    };
    ko.applyBindings(viewModel);
</script>

说明:以上代码的效果是的,当input的值发生变化的时候,会给viewModel的name属性重新赋值,并在p标签中显示

2.3 visible绑定

visible绑定到DOM元素上,使得该元素的hidden和visible状态取决于绑定的值
例如:

<div data-bind="visible:shouldShowMessage">
    当visible的值为true的时候,你才可以看到这句话
</div>
<script type="text/javascript">
  var viewModel={
        shouldShowMessage:true 
    };
    ko.applyBindings(viewModel);
</script>

说明:当visible的值为真的时候,元素的display为css设置的样式。 当visible的值为假的时候,元素的display为none,它的优先级高于css定义的任何display的样式

2.4 html绑定

html绑定到DOM元素上,使得该元素的HTML值为你绑定的参数值
例如:

<div data-bind="html:content">
    <p>原来的html结构</p>
</div>
<script type="text/javascript">
    var viewModel={
        content:'<h1>标题一</h1>'
    };
    ko.applyBindings(viewModel);
</script>

说明:knockout将参数值设置到指定DOM元素的innerHTML属性上,元素原来的内容将会被覆盖

2.5 css绑定

css绑定是添加或删除一个或多个class类名到指定DOM元素上
例如:

<style type="text/css">
    .bg{
        background: red;
    }
</style>
<div data-bind="css:{bg:flag}">
    <p>css绑定</p>
</div>
<script type="text/javascript">
    var viewModel={
        flag:true
    };
    ko.applyBindings(viewModel);
</script>

说明:以上代码的效果是当flag为true的时候,将bg这个类添加到指定DOM元素上。当flag为false的时候,将bg这个类从指定DOM元素上移除

2.6 style绑定

style是添加或删除一个或多个DOM元素上的style值
例如:

<div data-bind="style:{background:flag?'red':'blue',color:flag?'blue':'red'}" >
    <p>内容</p>
</div>
<script type="text/javascript">
    var viewModel={
        flag:true
    };
    ko.applyBindings(viewModel);
</script>

说明:以上代码的效果是当flag为true的时候,DOM元素为红背景蓝字体。当flag为flase的时候,DOM元素为蓝背景红字体

2.7 attr绑定

attr绑定提供了一种可以设置DOM元素的任何属性的值,例如可以设置a标签的title属性,href属性,自定义属性等等

<a data-bind="attr:{href:url,title:alt,self:self}">百度</a>
<script type="text/javascript">
    var viewModel={
       url:'https://www.baidu.com/',
       alt:'百度一下',
       self:'自定义属性值'
    };
    ko.applyBindings(viewModel);
</script>

说明:以上代码的效果是给a动态绑定了href和title属性已经自定义属性self的值

2.8 checked绑定

checked绑定可以用于checkbox和redio设置的绑定
例如:

<input type="checkbox" data-bind="checked: checkboxFlag"/>
<input type="radio" data-bind="checked: radioFlag" value="radioValue"/>
<script type="text/javascript">
    var viewModel={
        checkboxFlag: true,
        radioFlag:'radioValue'
    };
    ko.applyBindings(viewModel);
</script>

说明:以上代码的效果是,当checkboxFlag为true的时候,对应的checkbox被选中。当radioFlag的值等于radio的value的值的时候,对应的元素被选中

2.9 click绑定

click绑定在DOM元素上,给元素添加点击事件

<button data-bind="click: clickFn">Click</button>
<script type="text/javascript">
    var viewModel = {
        clickFn: function () {
           alert();
        }
    };
    ko.applyBindings(viewModel);
</script>

说明:以上代码的效果是,当点击按钮的时候执行函数

2.10 event绑定

event绑定在DOM元素上,给元素添加指定的事件

<div data-bind="event: { mouseover: overFn, mouseout: outFn }">
    Mouse 
</div>
<script type="text/javascript">
    var viewModel = {
        overFn: function () {
            alert('over');
        },
        outFn:function(){
            alert('out');
        }
    };
    ko.applyBindings(viewModel);
</script>

说明:以上代码的效果是,当鼠标划入或划出的时候执行对应的函数

2.11 submit绑定

submit绑定在form表单上添加指定的事件,以使该form被提交的时候执行指定的函数

<form data-bind="submit: sub">
    ... form contents ...
    <button type="submit">submit</button>
</form>
<script type="text/javascript">
    var viewModel={
        sub: function (formElement) {
            console.log(formElement);
        }
    };
    ko.applyBindings(viewModel);
</script>

说明:submit只能用在form表单上,当你使用submit绑定的时候, knockout会阻止form表单默认的submit动作。sub执行的时候,knockout会将整个form表单元素传递到sub函数中

2.12 options绑定

options绑定控制下拉列表的内容,只能用于select元素

<select data-bind="options: options"></select>
<script type="text/javascript">
    var viewModel = {
        options: ['France', 'Germany', 'Spain']
    };
    ko.applyBindings(viewModel);
</script>

说明:options给select标签增加了options元素,options默认会被展开
#3.Knockout的数据绑定

3.1. 单向绑定

<p>姓名:<span data-bind="text:name"></span></p> 
<p>年龄:<span data-bind="text:age"></span></p>
<script type="text/javascript">
 var viewModel= {
      name:'lsz';
      age:'23';
  }
  ko.applyBindings(viewModel);
</script>

3.2. 双向绑定

<p>姓名:<span data-bind="text:name"></span></p>
<p>年龄:<span data-bind="text:age"></span></p>
<p>姓名:<input type="text" data-bind="value:name"></p>
<p>年龄:<input type="text" data-bind="value:age"></p>
<script type="text/javascript">
  var viewModel={
        name:ko.observable('lsz'),
        age:ko.observable('23')
    };
    ko.applyBindings(viewModel)
</script>

说明:上述代码中,利用ko.observable()实现对属性的监控,当input的值发生改变的时候,span的值也会发生改变

3.3. 依赖绑定

<p>姓名:<span data-bind="text:name"></span></p>
<p>年龄:<span data-bind="text:age"></span></p>
<p>姓名:<input type="text" data-bind="value:name"></p>
<p>年龄:<input type="text" data-bind="value:age"></p>
<p>姓名年龄:<span data-bind="text:nameAge"></span></p>
<script type="text/javascript">
  var viewModel={
        name:ko.observable('lsz'),
        age:ko.observable('23')
    };
    viewModel.nameAge=ko.computed(function () {
        return this.name()+this.age();
    },viewModel);
    ko.applyBindings(viewModel);
</script>

说明:ko.computed()用来依赖一个或多个监控属性,并且当其中任何一个依赖对象被改变的时候将会自动更新。

未完待续

团队博客规范

1.博客内容直接在这里开issue发布,使用markdown语法编写。
2.每人每个星期必须发布一篇文章
3.文章内容可以是以下几种类型

  • 技术使用方法或经验
  • BUG分析和解决
  • 技术调研分析
  • 工作思考或感悟
  • 其他

4.每周一9点检查上周的文章发布情况,没有写的同学需要在微信群发50元红包
5.对其他同学写的文章,可以在issue下留言回复

2016/05/20 iUAP-Design初步规划

#2016-05-20 工作日志

基于上次的梳理,大致的思路已经成型,但是就UI框架等具体的落地和一些概念的共同理解,其实这些都没有一个很好的共识。而且,上一次的梳理,更多的是我们站在技术角度的去思考,用什么样的技术元素去实现。所以,这次找到刘鸿溶同学,我们的设计师,一起交流讨论。

1. 关于iUAP Design这个概念

从最早提出,到领导和同事们之间口头的传播,甚至都没有一个明确的认知,到底iUAP Design是一整套的技术的集合,一个集大成者,还是只是一种设计语言,一个通用的设计模式,这是存在疑问的,所以我们带着疑惑去了解Material Design和Ant Design。

Material Design是谷歌推出的全新的设计语言,这种设计语言旨在为手机、平板电脑、台式机和“其他平台”提供更一致、更广泛的“外观和感觉”。过去Google的产品线,每一个都相当的独立,在产品的设计上反映得尤为明显,甚至不必看产品设计,只要看一下Google每款产品的LOGO都能发现许多不同风格的设计。这种混乱难以体现出Google的风格,如果Google的风格不是混乱和无序的话。所以谷歌的想法是让谷歌平台上的开发者掌握这个新框架,从而让所有应用就有统一的外观,就像是苹果向开发者提出的设计原则一样。

好了,到这里,我们对Design就应该有了一个清晰的认知。

再来看Ant Design,Ant Design 是一个致力于提升『用户』和『设计者』使用体验的中台设计语言。蚂蚁金服也是在内部众多产品的研发过程中面临着不同产品风格迥异的困惑。在中台产品的研发过程中,会出现不同的设计规范和实现方式,但其中往往存在很多类似的页面和组件,给设计师和工程师带来很多困扰和重复建设,大大降低了产品的研发效率。经过大量的项目实践和总结而沉淀出一个中台设计语言 Ant Design,就是为了统一中台项目的前端 UI 设计,屏蔽不必要的设计差异和实现成本,解放设计和前端的研发资源。

所以我们对iuap design的定义有两层含义:

  • iuap design是由用友网络FED团队打造的前端集成解决方案,为企业应用开发而生,iuap design提供从产品界面设计到前端开发的完整开发生态,可以大大提升设计和开发的效率。
  • iuap design是面向互联网企业的 UI 设计语言

2. 大局观

大家在前前后后的讨论中,都有一个基本**,希望去降低开发成本,提升开发效率,并且建立技术生态,向公司内外推广,帮助更多的技术团队,减轻开发压力,提升产品质量,统一产品外观。

经过讨论,我们明确了这个技术生态的产品维度。假如把我们的产品开发类比为盖房子,而且要盖很多的房子,并且批量盖出的房子速度要快,质量要好,风格要统一,我们应该要怎么做。

  1. 房屋设计(房间的大布局) -- 设计规范
  2. 房间里需要不同的家具,完成不同的功能 -- 各种组件和插件
  3. 需要装修工进行房间的装修 -- 设计师和开发者实现
  4. 需要加快装修进度,保证装修质量 -- UI框架
  5. 房间内茶桌是什么样的,窗帘是怎么设计的 -- 开发实例
  6. 单个房间应该怎么装修,卧室要装修成什么样 -- 典型页面
  7. 我们最终需要设计出一个样板房,给其他套房的装修提供一个快速的参考标准。 -- 模板库
  8. 样板房有了,但是开发商要建设的一个小区,给一栋楼装修,这是基于不同的样板房集成起来的 -- 优站精选

**前四个选项:**这是我们在UI框架这个层次需要做的事情
**后四个选项:**这是我们在技术生态这个层次来做的事情

3. UI框架

我想,上面的阐述应该是我们后续开发过程中的认知基调,iUAP Design是设计语言,我将会在这个设计语言的指导下,开发实现一个我们的UI框架,这个框架的名字也取名为iUAP Design,当然,我觉得也有些不妥,应该换一个名字,比如iUAPUI,是否会更加合理一些。

在更改之前,继续使用iUAP Design作为我们UI框架的名字,同时也是设计语言。而且,后续还会有这方面的话题讨论,比如我们整个开发生态里面其他技术产品的命名,是否iUAP Design作为整套技术产品的集合,这也有待讨论。

3.1 UE规范

后续如何开发,将由我们的设计师提出完整统一的UE规范,包括交互模式、配色方案、设计原则等等。FED团队负责将这个规范进行框架的实现,这是我们基调。

3.2 全局CSS

  • 初始化样式reset
    • html 标签重置
  • 全局css样式base
    • 布局
    • 栅格
    • 排版:字体,粗细,类型
  • layout.css
    • 不同的导航,导航算是组件还是布局?
    • 页面切割
    • 维度:有无图标,推屏,还是直出

3.3 对控件、组件、插件几个概念的理解

  • 控件
    • html+css
    • 不同状态下的样式集合
  • 插件
    • js+css
    • 大功能或小功能
  • 组件
    • css组件
    • html+css+js 组件库

3.4 CSS基础组件和扩展组件

css组件样式

  • 图标字体
    • 合并,基础库(做好),扩展库
    • 统一,提供
  • 基础组件样式 默认的(横向的) 优先级 默认 u.base.css
    • navbar navbar-default navbar-inverse
    • 默认提供10个
  • 对css组件样式的扩展:样式板(纵向的) u.red.css
    • 什么样的方式
    • 和主题是联动的
    • 每个组件的不同的可能方式):多少个组件,哪些

框架提供的最终的css样式文件:u.base.css和u.xx.css(比如u.red.css、u.extend.css,这些提供出去的文件是可修改的,也可定制的,是在css层面纵向的扩展,样式板的能力就是在这个层面的)

3.5 基础js插件和扩展js插件

  • 基础部分包括我们现有的
  • 扩展部分可以为第三方或是业务部门高质量易用的插件

3.6 定制化能力和主题

定制化分为两个层面

  1. 资源打包的定制,选择什么样的js插件和css组件进行打包
  • 插件的定制。打包方式
  • CSS组件级定制
  • 颜色变化
  • 交互模式
  • 样式:形状,大小,尺寸

样式板和主题(定制)是联动的,一起的。或者说,主题其实就是样式板和插件的集合?

4. 基于UI框架的技术生态

基于第二部分大局观的讨论,技术生态的格局和阶梯工作都基本明确,在大方向上是明确的,这里一一讨论稍微细粒度的内容。

但是,我们的共识应该是:技术生态的开展,都要基于我们的UI框架,这是基础。

4.1 小维度:开发实例

开发实例是一些代码片段或是组件的集合,我们提供

4.2 中纬度:典型页面

典型页面

  • 单个页面
  • 布局维度
  • 左树右表等

4.3 大维度:模板库

模板库:含有相关功能的一组页面或多个页面的集合,比如登录注册等合起来是登录模板

4.4 领域维度:优站精选

我们前期可以从这件事情,可以先从这几个产品方向开始:

  • 电商
    • 登录注册模板
    • 商品展示模板
    • 404错误
    • 支付模板
    • 再加上设计师的参与
  • sass
  • 运维类

后续真正完整的优站精选的生态,需要大概能得到以下方面的实现,但还是思路:

  • 可以实际看到
  • 可以试一试
  • 选一个主题风格
  • 生成二级域名
  • 可以付费或是免费直接代码下载
  • 我们打造的是体验式的优站精选
  • 我们提供支付渠道和方式
  • 我们提供开发教程,比如生成gif动画教程也是一个很不错的方式
  • 这是一个对客户的商业价值
  • 我们可以让设计师和开发者都参与到这个平台上面来

5. 如何开发

这么大的规划,如果按部就班,先框架后生态的开发,后者依赖前者的方式。很显然,这样的开发模式会比较笨拙,有了依赖后,开发不能一次性见到成效,而且需要一个很长的周期才能开发我们的成果。

所以,应该从另一个维度来进行思考,从纵向的维度来思考

  1. 我们先开发核心的组件和控件
  2. 有了这个以后组成一些开发实例,立马可用
  3. 用部分开发实例加上主题风格,形成几个经典页面
  4. 几个经典页面的集合,就是一个模板库

这样纵向的从某几个组件到模板,形成一个开发任务,每个阶段都需要有产出。

6. 推进

  • 部门全体封闭开发两周,包括我们的设计师,结果导向:UI框架产出1.0版本。
  • 各位手中需要支持的事情需要同步进行,比如解决BUG,但除此之后所有精力应该在1.0版本的框架上。

dropdown 源码解读

使用方式

引入文件

所需文件:
1、jquery.js
2、dropdown.js

代码引用

整个dropdown组件包裹在类为“dropdown”的容器里,内部有个‘dropdown-toggle’的a元素和样式为‘dropdown-menu’的ul元素。a元素是触发下拉菜单显示隐藏的toggle元素,ul元素是下拉菜单的内容

        <li class="dropdown">
            <a href="#" class="dropdown-toggle" data-toggle="dropdown">
               Java 
               <b class="caret"></b>
            </a>
            <ul class="dropdown-menu">
               <li><a href="#">jmeter</a></li>
               <li><a href="#">EJB</a></li>
               <li class="divider"></li>
               <li><a href="#">另一个分离的链接</a></li>
            </ul>
         </li>
        $(“. dropdown-toggle”).dropdown(‘toggle’)触发下拉菜单的展开或折叠

整理架构

整体**

通过data-toggle绑定触发事件的父集元素,点击添加或删除open样式类,由此展开或折叠下拉菜单;

构建dropdown类,当即触发click事件,调用dropdown.prototype.toggle方法,toggle发放中调用clearMenus函数折叠下拉框,下拉框折叠的情况触发if语句展开下拉框;

构造函数

         var backdrop = '.dropdown-backdrop'
      var toggle   = '[data-toggle="dropdown"]'
      var Dropdown = function (element) {
        $(element).on('click.bs.dropdown', this.toggle)
      }

      Dropdown.VERSION = '3.3.6'

          // 由触发元素dropdown-toggle调用dropdown方法,通过data属性new出一个dropdown实例,data属性也记录该对象,继而通过传入的option参数调用实例的toggle方法  
          function Plugin(option) {
        return this.each(function () {
          var $this = $(this)
          var data  = $this.data('bs.dropdown')

          if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
          if (typeof option == 'string') data[option].call($this)
        })
      }

      var old = $.fn.dropdown

      $.fn.dropdown             = Plugin
      $.fn.dropdown.Constructor = Dropdown

         // DROPDOWN NO CONFLICT
        // $.fn.dropdown已存在,使用old保存该值,$.fn.dropdown空闲出来完成操作,执行        $.fn.dropdown.noConflict回归原$.fn.dropdown  

      $.fn.dropdown.noConflict = function () {
        $.fn.dropdown = old
        return this
      }

事件

触发事件
  $(document)  
    .on('click.bs.dropdown.data-api', clearMenus)  //点击页面其他地方时,收起menu
    .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })  //假如下拉菜单里有表单元素时,通过冒泡阻止菜单的隐藏
    .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)  //自动注册dropdown组件
    .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)  //在toggle元素获取焦点后,允许按向下箭头展开菜单
    .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown)  //允许在展开菜单后,可以通过向上向下箭头,切换菜单项
用户可自定义事件函数

包裹元素dropdown中设置show(展开前)、focus(展开中)、shown(展开后)、hide(隐藏前)、hidden(隐藏后)事件,show和hide事件中可以阻止默认事件发生。

菜单展开前的函数定义
$('#myDropdown').on('show.bs.dropdown', function () {
  // do something…
})

心得体会

  • 事件绑定方式
    平时写代码时,如果给某个按钮添加事件会这样写:
$('button').on("click", function (event) { 
    alert(1);
});

而bootstrap中将事件都绑定到了document上。

$(document).on('click.bs.btn','button',function (e){};

中间多了一个参数,而且选择器变成了document。它的好处是只在document上绑定一个单击事件,利用冒泡的机制,在单击的时候检查是否是button元素,如果是才处理。而不是每个button都绑定事件,性能会提高很多

  • 代码格式学习

Node调试

本次主要介绍基于chrome浏览器对node进行调试。

安装插件

基于chrome浏览器调试需要安装node-inspector

npm install -g node-inspector

启动调试监听

 node-inspector

调试监听默认占用8080端口,修改端口可以通过

node-inspector --web-port 3456

启动node程序

 node --debug app.js

上面的方式不会在程序第一行停留,要在程序第一行停留可通过以下方式启动

 node --debug-brk=5858 gulp.js

chrome调试

在chrome浏览器中打开以下链接:

 http://127.0.0.1:8080/?port=5858

JS实现继承的五种方式

对象冒充

function Parent(username) {
    this.username = username;
    this.hello = function() {
        alert(this.username);
    }
}

function Child(username, password) {
    //通过以下3行实现将Parent的属性和方法追加到Child中,从而实现继承
    //第一步:this.method是作为一个临时的属性,并且指向Parent所指向的对象,
    //第二步:执行this.method方法,即执行Parent所指向的对象函数
    //第三步:销毁this.method属性,即此时Child就已经拥有了Parent的所有属性和方法
    this.method = Parent;
    this.method(username); //最关键的一行
    delete this.method;

    this.password = password;
    this.world = function() {
        alert(this.password);
    }
}
var parent = new Parent("zhangsan");
var child = new Child("lisi", "123456");
parent.hello();
child.hello();
child.world();

call() 方法方式

call方法是Function类中的方法
call方法的第一个参数的值赋值给类(即方法) 中出现的this
call方法的第二个参数开始依次赋值给类(即方法) 所接受的参数

function test(str) {
    alert(this.name + " " + str);
}
var object = new Object();
object.name = "zhangsan";
test.call(object, "langsin"); //此时,第一个参数值object传递给了test类(即方法)中出现的this,而第二个参数"langsin"则赋值给了test类(即方法)的str

function Parent(username) {
    this.username = username;
    this.hello = function() {
        alert(this.username);
    }
}

function Child(username, password) {
    Parent.call(this, username);

    this.password = password;
    this.world = function() {
        alert(this.password);
    }
}
var parent = new Parent("zhangsan");
var child = new Child("lisi", "123456");
parent.hello();
child.hello();
child.world();

apply() 方法方式

apply方法接受2个参数,
A、 第一个参数与call方法的第一个参数一样, 即赋值给类(即方法) 中出现的this
B、 第二个参数为数组类型, 这个数组中的每个元素依次赋值给类(即方法) 所接受的参数

function Parent(username) {
    this.username = username;
    this.hello = function() {
        alert(this.username);
    }
}

function Child(username, password) {
    Parent.apply(this, new Array(username));

    this.password = password;
    this.world = function() {
        alert(this.password);
    }
}
var parent = new Parent("zhangsan");
var child = new Child("lisi", "123456");
parent.hello();
child.hello();
child.world();

原型链方式

子类通过prototype将所有在父类中通过prototype追加的属性和方法都追加到Child, 从而实现了继承

function Person() {}
Person.prototype.hello = "hello";
Person.prototype.sayHello = function() {
    alert(this.hello);
}

function Child() {}
Child.prototype = new Person(); //这行的作用是:将Parent中将所有通过prototype追加的属性和方法都追加到Child,从而实现了继承
Child.prototype.world = "world";
Child.prototype.sayWorld = function() {
    alert(this.world);
}

var c = new Child();
c.sayHello();
c.sayWorld();

混合方式

混合了call方式、 原型链方式

function Parent(hello) {
    this.hello = hello;
}
Parent.prototype.sayHello = function() {
    alert(this.hello);
}

function Child(hello, world) {
    Parent.call(this, hello); //将父类的属性继承过来
    this.world = world; //新增一些属性
}

Child.prototype = new Parent(); //将父类的方法继承过来

Child.prototype.sayWorld = function() { //新增一些方法
    alert(this.world);
}

var c = new Child("zhangsan", "lisi");
c.sayHello();
c.sayWorld();

iUAP Design 相关名词的官方解释

iUAP Design相关名词的官方解释

1. 用友网络FED团队

用友网络FED团队是用友网络iUAP前端技术部的简称,这是一支由专业的前端工程师组建的团队。我们崇尚开源自由,追求高效卓越,致力于打造企业互联网前端开发第一入口。我们是一群热爱编程热爱阳光的年轻人,希望通过技术让生活变得更加美好。

2. NeoUI

NeoUI是基于UI设计语言iuap design而打造的一个企业级前端UI框架,致力于快速构建标准易用、美观大气、多端适配的前端页面。

NeoUI包含强大的全局CSS样式、丰富的组件和插件,更为开发者提供不同领域的模板库集合,以及丰富的样式板;同时,还为非开发人员提供简单易用的页面设计器,让你的idea通过页面极速呈现。

3. iUAP Design

iUAP Design有两层解释。

  • 设计语言

iUAP Design是由用友网络iUAP UED团队根据多年设计经验沉淀总结的一套UI设计语言,iUAP Design注重产品逻辑交互的清晰,关注高效率的可用性、易用性 、 一致性,并提供悦目的审美展示。。

  • 前端集成解决方案

iUAP Design是由用友网络FED团队打造的一套前端集成解决方案,为企业级应用开发而生。提供前端开发全链路生态,包含设计指导、前端框架集、前端开发工具集、组件库、模板库等一系列技术产品。我们秉持开放、自由、轻量、生态的原则,提供可组装可插拔的技术生态。

4. Kero

Kero是一个扩展MVVM架构中数据模型的JS类库,实现数据和UI的双向绑定,帮助开发者快速构建数据驱动型应用,解决复杂数据交互问题

6月12-6月17日~工作思考篇

完成的任务

1、项目上的支持(checkbox、tree、dataTable问题)

2、基础控件文档

  目前已经丰富了基本控件的一些样式。例如 button、checkbox、分页等控件都提供了不同色彩、不同尺寸的样式等。

感悟

  最近这一周工作量比较大,对于同时有多个任务要处理的情况比较多,那如何安排优先级呢?

1、清晨确定一天的工作计划

2、根据今天的任务,安排优先级。

  • 核心任务并且紧急的优先级为10,可以放在上午做
  • 核心任务并且非紧急的优先级为9.
  • 非核心并且紧急的优先级为7,可以利用琐碎时间解决
  • 非核心并且非紧急的优先级为5,可以延缓执行。

  核心任务有:

1、所有的基础控件文档可以在官网正常显示;

2、框架基础样式的梳理以及补充;

  紧急任务有:

1、支持其他项目;

2、bug每日清除;

Gulp配置

gulp基本介绍

Gulp是基于node.js的构建工具

gulp安装

npm install -g gulp

gulp配置文件

正常使用gulp前,需要先检查下是否已存在package.json文件,如未存在,可使用以下命令建立

npm init

完成以上操作,新建gulp配置文件gulpfile.js,以下针对gulp配置进行说明。

gulp可通过一些列插件完成文件的压缩,合并,重命名,图片压缩,base64 等构建功能。一些插件的功能介绍:

gulp组件功能介绍

gulp-imagemin: 压缩图片
gulp-ruby-sass: 支持sass
gulp-minify-css: 压缩css
gulp-jshint: 检查js
gulp-uglify: 压缩js
gulp-concat: 合并文件
gulp-rename: 重命名文件
gulp-htmlmin: 压缩html
gulp-clean: 清空文件夹

gulp插件安装

npm install --save-dev gulp-util gulp-imagemin gulp-ruby-sass gulp-minify-css gulp-jshint gulp-uglify gulp-rename gulp-concat gulp-clean gulp-livereload

使用npm包管理器安装。

gulp基本api

  • Gulp.task(name, fn) 用来定义任务的
  • Gulp.run(tasks…) 从3.5开始弃用,将在4.0中删除。https://github.com/gulpjs/gulp/blob/master/index.js#L16
  • Gulp.src(glob) 用来读取文件
  • Gulp.dest(folder) 用来写入文件
  • Gulp.watch(glob, fn) 用来监听文件是否改动过

以如下文件重命名示例说明:

Demo-文件重命名

var gulp = require('gulp'); // 引入gulp
var rename = require('gulp-rename'); //引入插件gulp-rename

// 定义任务
gulp.task('name', function(){
  return gulp.src('./js/index.js') //获取需要执行rename的文件
    .pipe(rename('main.js')) //执行重命名
    .pipe(gulp.dest('./dist/js')) //重命名后的文件输出到'./dist/js'文件下
})

以上demo简述:

  • 引入gulp及所需的插件gulp-rename
  • 定义任务gulp.task名为name
  • 读取需要重命名的js文件./js/index.js
  • 执行重命名,修改为main.js
  • 将修改后的main.js输出到./dist/js目录下。

如上,定义了一个简单的示例,在命令行输入

gulp name

执行demo示例

链式任务执行

实际情况中,经常会遇到,一个任务是基于另外一个或多个任务完成后才能执行。

写法如下

gulp.task('default', ['less','watch'], function(){
  // `less`,`watch`任务结束后执行此部分
  do something
});

说明,以上default任务执行时,gulp会先执行之前定义的less,watch任务后,才会执行function(){}部分,注意lesswatch任务执行没有前后顺序

以下是之前做的一个基本配置,后续会完善gulp更多功能说明

gulp配置

未完。

2016/05/19 - 工作整体规划

YYFED 2016-05-19工作日志

公司新成立了iUAP前端技术部(FED),主要职责有:负责公司前端框架开发、提供工具库集合、提供解决方案、开发公司前端框架、提供模板库资源、承担公司前端技术交流和培训的工作、提供精选优站资源。

期望很高,理想很丰满,现实很骨感,因为目前我们只有六个小伙伴,其中只有四个能投入进来,而且每个人都还担着其他的事情,所以大概每人只有一半的时间投入上来。换句话,我们综合起来只有2个人的全职时间投入到基础框架的开发上。

1. 说一下背景

成立新部门的初衷是公司在前端工程化能力上的缺失,无论是什么样的描述,我觉得工程化能力是最好的体现。那么最直观的体现是什么呢,领导看到各条业务线都在吭哧吭哧的做网站,写页面,而且都还那么丑。这还能忍?所以这也是我们当前需要聚焦并且紧急的问题。

2. UI 框架

基于这样的背景,首先需要考虑做的事情,便是在UI框架上去进行聚焦突破。也许大家会想,这还不好办,那么多开源而且炫酷叼炸天的UI框架和模板库,拿来用就是了,何苦去造那么多轮子。真实的需求是:

  • 开源的框架在交互体验上和企业开发是有差距的,无法完全满足需求
  • 插件的缺失,有很多开源插件,但是企业级应用,比较复杂,开源产品无法满足
  • 业务线缺前端,直接用开源的,出了问题,或是功能缺少,不好玩,用公司的还可以有我们这群苦逼的人给一起出谋划策。

其实UI框架是一个事,但不是全部,只能说是冰山一角,比如整套前端解决方案、工具库等等。那就是后面的事情了,这里暂且按下不表。

3. 讨论分析

方向有了,目标也有了,具体如何从天上到地下,把想法都落实下来,这个是关键。我是深有体会的,当初一个项目,也是基于各个领导的规划和讨论,最终写代码的只有我一个,哎,说多了都是泪。

那么具体应该怎么搞呢。

  • 全局的 CSS 样式
  • 框架基础样式:CSS组件
  • 扩展的CSS样式板
  • 框架核心插件,以及扩展插件
  • 提供开发实例,能够快速生成代码,并看到效果
  • 提供框架定制能力,能够让开发者只打包需要的资源
  • 图标字体库,也是必不可少的
  • 网站博客,做了那些工作,具有什么功能,不用网站展示出来,那别人也不知道怎么用怎么玩啊
  • 基于框架产出默认的几套主题风格,日韩的,欧美的,地中海的...噢,不对,是适用于不用产品的网站配色风格
  • 优站精选,不来几套基于UI框架好用的示例站点,也看不出你做的多棒啊,所以,还得搞几套,秀一下,也许别人正好想用呢

多的也不说了,满满当当的也罗列了不少。等等,让我安静待会,有点懵,这么些东西,别说要多么高的技术,就是体力活,也得安排出个轻重缓急,日期排程。

4. 如何推进

这些需求,完成了,对产品开发可以快速提高开发速度。具体如何推进,需要结合当前现状。

4.1 关于UUI

UUI是一个轻量级企业应用前端控件库,分为UI插件和基于ko封装的biz两大部分,致力于解决前端复杂交互问题。

公司内外有大小20多个团队在使用,帮助企业在前端开发上解决了很多问题,但同时也暴露出了不少问题,比如样式不够美观大气、不够开放自由、文档不够齐全等,那么,这些都是需要解决的。

同时,我们接下来要做的UI框架也是基于此来演进的。

4.2 UUI中UI部分和Datatable相分离

UI是交互和样式部分,Datatable是基于knockout这个古老的MVVM前端框架上的再一次封装,解决企业应用中复杂数据传输的问题,同时也提供了很多基于ko的插件。

而在新的规划中,我希望能够更加纯粹一点。UI框架应该是独立的,Datatable部分也应该是独立的,单独或组合提供出去使用都可以。

但基于时间成本和维护成本,暂时先不考虑分离,原来的继续维护,UI框架部分重新建一个Git仓库进行开发和维护。

4.3 技术元素

主意已决定了,在Github上新建一个Git库进行开发和维护。那么接下来的事情是,如何进行这个UI框架的搭建。

技术元素:

  • sass 用sass来作为css预处理器
  • gulp 使用gulp作为构建工具,打包
  • es6 支持使用es6语法写插件
  • babel 解析es6语法
  • jekyll 网站博客的建设
  • eslint代码规范
  • bower包的发布
  • npm包依赖管理和包的发布
  • 测试:浏览器兼容性测试和覆盖测试
  • autoplefixer 浏览器后缀

4.4 目录规划和构建

项目目录:

  • js
    • a.js
  • scss
    • a.scss
    • extend
      • b.scss
  • plugin
    • a
      • a.js
      • s.scss
  • docs
    • dist
    • jekyll资源
  • fonts
    • a.eot
  • dist
    • js
    • css
      • u.css 全局样式和组件样式
      • u.extend.css CSS样式板
    • fonts

4.5 需要开展的工作

  • 全局css样式
    • 定义能力:布局,栅格,响应式,排版,工具类....
    • 参考bootstrap
    • 补充哪些
  • 核心css样式板(加强)u.css 框架基础样式
    • bootstrap样式参考
    • 能力不够
    • css3 动画 animate
  • 扩展css样式板:u-extend.css 扩展的css样式板
    • 提供使用的组件样式,能力增强
  • 核心插件
    • 哪些放在核心插件:20个(原生js)
    • image 图标字体
    • 图标字体(推动):炫酷网站,颜色色彩,图标,动画,合理的布局
  • 扩展插件plugin
    • grid
    • tree
    • 第三方
    • 打包,调用到,concat,u.date();
    • ue效果

4.6 开发实例

详细的开发实例,所见即所得,基于提供的UI框架快速生成页面模板

4.7 定制能力

样式和插件可定制打包

4.8 网站博客

使用jekyll搭建,需要整合现有的资源

5. 具体任务分配

  • 王皓:50%时间,工作内容: 样式板
  • 郭永峰:60%时间,工作内容: 搭建
  • 丁锐锋:50%时间,工作内容: 迁移,文档和ppt
  • 田孝启:不定,数字营销
  • 刘月凯:50%时间,工作内容: grid控件,拆分,ue,共享服务
  • 刘认华:30号投入

6. 脚手架

脚手架提供基础UI框架、NODE开发服务、模块化方案、框架(React,ko,vue,angular,backbone),iuap-init

提供以下技术内容:

  • 完整技术选型的脚手架
  • 培训
  • 开发方式指导

第一周工作感悟

来iUAP前端技术部整整一周了。今天风和日丽,是个记流水账的好日子。

目前还是熟悉阶段,所做的工作是控件的整理和官网文档的整理。一周整下来,整个工作模式是 这个文件夹的文件复制到那个文件,再执行个命令,看下效果,然后提交,然后一堆莫名要提交的东东。。。。啊。。。仰天长啸啊!!项目好几个,也蛮大,我涉及的是其中冰山一角,对整个项目有种不识庐山真面目的赶脚。

路漫漫其修远兮。。。需要学习的东西好多好多。

下一步需要慢慢熟悉我们官网的运作流程,慢慢涉及JS的工作。keep fighting!

浏览器兼容之浏览器版本处理

前端开发过程中,浏览器的兼容处理是不可避免的工作,本次主要在HTML、CSS、JS三个方面对于判断浏览器版本进行说明

HTML

在HTML代码中主要以识别IE版本为主,以下为示例

<!–[if IE 6]> 仅IE6可识别 <![endif]–>
<!–[if lte IE 6]> IE6及其以下版本可识别 <![endif]–>
<!–[if lt IE 6]> IE6以下版本可识别 <![endif]–>
<!–[if gte IE 6]> IE6及其以上版本可识别 <![endif]–>
<!–[if gt IE 6]> IE6以上版本可识别 <![endif]–>
<!–[if IE]> 所有的IE可识别 <![endif]–>
<!–[if !IE]><!–> 除IE外都可识别 <!–<![endif]–>

CSS

CSS 代码中,则可以根据各浏览器自己独立的可识别的特殊代码来编写区分浏览器的代码,例如为IE系列浏览器可读[\9],而IE6和IE7可读[*],另外 IE6可辨识 _ ;由于CSS读取时是按从上到下来的,同样属性靠后写的生效,因此可以依照顺序写下来,就会让每个浏览器正确的读取到自己看得懂得CSS语法。

.classname{
    background:blue; /*所有浏览器都可以识别*/
    background:red \9; /*IE8 背景变红色*/
    *background:black; /*IE7 背景变黑色*/
    _background:orange; /*IE6 背景变橘色*/
}

.classname {
    background:black !important; /*非IE6 背景变黑色*/
    background:orange; /*IE6 背景变橘色*/
}

JS

在js代码中可以通过navigator.userAgent来判断浏览器版本的具体信息,如果上面的方式都不能很好的满足需求,在js中可以精确到浏览器的具体版本,可以更加精确的进行处理。以下为iuap-design中对于浏览器版本信息的处理。

var userAgent = navigator.userAgent,
        rMsie = /(msie\s|trident.*rv:)([\w.]+)/,
        rFirefox = /(firefox)\/([\w.]+)/,
        rOpera = /(opera).+version\/([\w.]+)/,
        rChrome = /(chrome)\/([\w.]+)/,
        rSafari = /version\/([\w.]+).*(safari)/,
        version,
        ua = userAgent.toLowerCase(),
        s,
        browserMatch = { browser : "", version : ''},
        match = rMsie.exec(ua);

if (match != null) {
    browserMatch =  { browser : "IE", version : match[2] || "0" };
}
match = rFirefox.exec(ua);
if (match != null) {
    browserMatch =  { browser : match[1] || "", version : match[2] || "0" };
}
match = rOpera.exec(ua);
if (match != null) {
    browserMatch =  { browser : match[1] || "", version : match[2] || "0" };
}
match = rChrome.exec(ua);
if (match != null) {
    browserMatch =  { browser : match[1] || "", version : match[2] || "0" };
}
match = rSafari.exec(ua);
if (match != null) {
    browserMatch =  { browser : match[2] || "", version : match[1] || "0" };
}
if (match != null) {
    browserMatch =  { browser : "", version : "0" };
}


if (s=ua.match(/opera.([\d.]+)/)) {
    u.isOpera = true;
}else if(browserMatch.browser=="IE"&&browserMatch.version==11){
    u.isIE11 = true;
    u.isIE = true;
}else if (s=ua.match(/chrome\/([\d.]+)/)) {
    u.isChrome = true;
    u.isStandard = true;
} else if (s=ua.match(/version\/([\d.]+).*safari/)) {
    u.isSafari = true;
    u.isStandard = true;
} else if (s=ua.match(/gecko/)) {
    //add by licza : support XULRunner
    u.isFF = true;
    u.isStandard = true;
} else if (s=ua.match(/msie ([\d.]+)/)) {
    u.isIE = true;
}

else if (s=ua.match(/firefox\/([\d.]+)/)) {
    u.isFF = true;
    u.isStandard = true;
}
if (ua.match(/webkit\/([\d.]+)/)) {
    u.isWebkit = true;
}
if (ua.match(/ipad/i)){
    u.isIOS = true;
    u.isIPAD = true;
    u.isStandard = true;
}
if (ua.match(/iphone/i)){
    u.isIOS = true;
    u.isIphone = true;
}

if((navigator.platform == "Mac68K") || (navigator.platform == "MacPPC") || (navigator.platform == "Macintosh") || (navigator.platform == "MacIntel")){
    //u.isIOS = true;
    u.isMac = true;
}

if((navigator.platform == "Win32") || (navigator.platform == "Windows") || (navigator.platform == "Win64")){
    u.isWin = true;
}

if((navigator.platform == "X11") && !u.isWin && !u.isMac){
    u.isUnix = true;
}
 if((String(navigator.platform).indexOf("Linux") > -1)){
u.isLinux = true;
}

if(ua.indexOf('Android') > -1 || ua.indexOf('android') > -1 || ua.indexOf('Adr') > -1 || ua.indexOf('adr') > -1){
u.isAndroid = true;
}

u.version = version ? (browserMatch.version ?  browserMatch.version : 0) : 0;
if (u.isIE) {
    var intVersion = parseInt(u.version);
    var mode = document.documentMode;
    if(mode == null){
        if (intVersion == 6 || intVersion == 7) {
            u.isIE8_BEFORE = true;
        }
    }
    else{
        if(mode == 7){
            u.isIE8_BEFORE = true;
        }
        else if (mode == 8) {
            u.isIE8 = true;
        }
        else if (mode == 9) {
            u.isIE9 = true;
            u.isSTANDARD = true;
        }
        else if (mode == 10) {
            u.isIE10 = true;
            u.isSTANDARD = true;
            u.isIE10_ABOVE = true;
        }
        else{
            u.isSTANDARD = true;
        }
        if (intVersion == 8) {
            u.isIE8_CORE = true;
        }
        else if (intVersion == 9) {
            u.isIE9_CORE = true;
        }
        else if(browserMatch.version==11){
            u.isIE11 = true;
        }
        else{

        }
    }
}
if("ontouchend" in document) {
    u.hasTouch = true;
}
if(u.isIOS || u.isAndroid)
    u.isMobile = true;

[移动]混合式app真机调试

在做混合式App/web app 通常一般开发时会通过chrome或者android/ios模拟器进行调试。
但实际上有些问题在chrome和模拟器上跑的好好的,放到真机上就见了鬼了。所以怎么在真机上调试就真是一件很头疼的事,以下结合自己在做移动应用时的实践经验简单分享一下。

  • Android调试
    拿数据线把手机连上你的电脑,然后允许usb调试,然后打开chrome,浏览器中输入chrome://inspect 然后在列表中找到你的设备,点击inspect(我也忘了具体是不是这个按钮了),然后就可以打开chrome开发者工具进行愉快的调试了。
    要求
    Android>4.0
    Chrome>32
    打开手机中的开发者选项中的USB调试
    不过好像貌似第一次需要翻墙然后会自动下载驱动。。。反正做前端的还是都去买一个翻墙代理吧
  • IOS调试
    这个需要有一个前提:
    你需要有一个mac。对的 需要一个mac。
    然后把你的iphone连上你的电脑,打开手机safari,输入你要访问的网址,然后在mac的safari-开发 下打开你的设备,默认会列出当前你手机访问的网址,就可以借助safari的调试工具进行真机调试了。

以上,不论是直接用chrome访问,还是打开app使用app里内嵌的webview 都是可以做到真机调试的。
如果有遇到问题欢迎反馈。
希望对大家有用。

iUAP Design 设计语言

iUAP Design 设计语言

在企业级应用开发中,因为产品的项目化实施产品及定客户制化程度较高,在实现过程中尤其是前端的实现,会有不同的设计及交互规范,然而这里规范中存在大量的雷同控件、页面、交互模式及实现方式,这些重复性的工作对产品参与人员,尤其是设计、开发等角色来说是个困扰和折磨,也带来了巨大的开发及管理成本,降低了互联网时代的开发效率。iUap Design是沉淀用友服务企业20年技术经验,总结而出的一个企业互联网应用设计语言。

iUap Design 开放 自由 轻量 生态 ,旨在重塑企业应用研发模式,解放设计,整合资源,提高效率,让研发团队更关注业务、效率提升用户体验。

iUap Design致力于提升”终端用户”、“UE设计”、“弱前端开发”等角色的使用体验。它模糊了研发角色(产品、交互设计师、UI设计师、前端开发工程师)原本清晰的界限,使用该语言,可以高效率快速产生体验一致、直接可用的前台最终html页面。

使用iUap Design可以快速地对整个产品前端界面进行深度定制,使整个界面保持高度一致而无需让工程师在此耗费更多精力,用户所需要做的就是下载iUap Design框架,然后开工就可以了。用户可以参考这套系统的详细文档说明及丰富的样式版、模版,快速生产符合iUap生态的优质应用,轻松地调用iUap Design核心视觉组件和交互模式,产出最佳方案。

iUap Design 特性

细微:

原子化设计方法解构界面元素

iUap Design提供精心设计的极简控件、组件

多维:

服务设计关注全方位体验

iUap Design尽可能多的的关注用户体验地图上的触点及角色

预知:

设计思维指引目标导向

使用iUap Design,参照样式版+模版库,可预知产品形态

iUap Design 设计原则

明确清晰

高效可靠

灵活简易

标准一致

主次有序

活力美观

iUap Design 设计基础

字体

排版

色彩

图标

文案

布局

动画

技术类产品标准文档格式

以下为用友FED团队文档的写作标准格式,适用于团队的的技术类产品,帮助用户更好更快的使用和认识我们的技术产品

文档的标题

1.安装或下载(download)

在这里说明获取资源的方式

整体的介绍,原理,相关的生态等

2.快速使用或起步(getting started)

基本的示例,一定要用简单并且推荐的方式给出示例,让用户一看就懂,粘贴复制就能运行使用。
示例的代码要完整,依赖到的资源要用代码或是文档的方式说清楚。

示例可以分层次,简单的,进阶的,综合的,都可以。

3.概述或介绍(instruction)

说清楚现状,我们为什么要做,我们所提供的框架、插件或是类库的原理是什么,功能特性是什么,适用的场景,能解决什么问题,等等,都可以扩展介绍,让用户更深入的了解和继续使用。

4.使用文档(document)

5.API介绍(API)

6.详细示例(Demo)

7.其他拓展标题

进阶体验,如何构建应用,对比市场其他同类技术产品等

自己动手写Knockoutjs - 实现计算属性和计算属性的依赖收集

1. 计算属性的定义

先来看下官方文档中计算属性如何使用的。

var myViewModel = { };
myViewModel.firstName = ko.observable("Bob");
myViewModel.lastName = ko.observable("Smith");
myViewModel.fullName = ko.dependentObservable(function () {
return myViewModel.firstName() + " " + myViewModel.lastName();
            })

参照observable的定义,先写出一个基本的。

ko.dependentObservable = function(evaluatorFunction, evaluatorFunctionTarget) {
    var _lastValue;

    function dependentObservable() {
        return _lastValue;
    }
    ko.subscribable.call(dependentObservable);
    return dependentObservable;
}

2. evaluate方法

evaluatorFunction是计算属性的计算方法,需要用它来给_lastValue重新赋值。通过call方法可以定义evaluatorFunction中的this
evaluatorFunction是计算属性所依赖的属性变化时需要执行的方法,它的执行需要注册到其依赖属性的subscribtiones中,并且还要通知这个计算属性的变化到依赖于它的计算属性。这样,我们需要一个包装方法evaluate,它是observable对象发生变化时的需要执行的subscribtiones
代码如下:

ko.dependentObservable = function(evaluatorFunction, evaluatorFunctionTarget) {
    var _lastValue;
    function evaluate() {
        _lastValue = evaluatorFunctionTarget ? evaluatorFunction.call(evaluatorFunctionTarget) : evaluatorFunction();

        dependentObservable.notifySubscribers(_lastValue);
    }

    function dependentObservable() {
        return _lastValue;
    }
    ko.subscribable.call(dependentObservable);
    evaluate();

    return dependentObservable;
}

3. 依赖收集的基本原理

计算属性我们定义好了,接下来就是依赖收集了。
依赖收集就是给observable对象(计算属性也算observable对象)注册计算属性的evaluate方法。
收集的时机就是在执行evaluate方法时,当调用observable对象的get方法时,需要将这个observable对象收集暂存起来,这样我们就得到了计算属性所依赖的observable对象的列表,然后我们需要给列表中的每个observable对象注册这个计算属性的evaluate方法。当所有的计算属性的evaluate方法都执行完一遍时,每个observable对象就都具有了依赖于它的计算属性的evaluate方法。

4. 收集暂存箱的实现

收集暂存箱需要在开始的时候初始化一个数组,结束的时候返回这个数组。

ko.dependencyDetection = (function () {
    var _detectedDependencies = [];
    return {
        begin: function () {
            _detectedDependencies.push([]);
        },

        end: function () {
            return _detectedDependencies.pop();
        },

        registerDependency: function (subscribable) {
            if (_detectedDependencies.length > 0) {
                _detectedDependencies[_detectedDependencies.length - 1].push(subscribable);
            }
        }
    };
})();

这里我们用一个闭包实现了变量的缓存,_detectedDependencies是一个长度为1的数组,用它的pop方法可以巧妙地实现在end的时候消除当前收集数组的依赖并返回这个数组。

5. 依赖收集的具体实现

首先在observable对象的get方法中用暂存器收集。

function observable(newValue) {
    if (arguments.length > 0) {
        // set 方法
        _latestValue = newValue;
        observable.notifySubscribers(_latestValue);
    } else {
        // get 方法
        ko.dependencyDetection.registerDependency(observable);
    }
    return _latestValue;
}
function dependentObservable() {
    ko.dependencyDetection.registerDependency(dependentObservable);
    return _lastValue;
}

所有的计算属性的依赖收集都只发生一次,就是在定义计算属性时,我们加入_isFirstEvaluation来标识是否是第一次。

var _lastValue,_isFirstEvaluation = true;
function evaluate() {
    _isFirstEvaluation && ko.dependencyDetection.begin();
    _lastValue = evaluatorFunctionTarget ? evaluatorFunction.call(evaluatorFunctionTarget) : evaluatorFunction();
    _isFirstEvaluation && replaceSubscriptionsToDependencies(ko.dependencyDetection.end());

    dependentObservable.notifySubscribers(_lastValue);
    _isFirstEvaluation = false;
}

给收集列表中的每个observable对象注册计算属性的evaluate方法。

function replaceSubscriptionsToDependencies(dependencies) {
dependencies.forEach(function(dependence) {
        dependence.subscribe(evaluate);
    });
}

计算属性和依赖收集算是mvvm中的核心和难点,具体实现代码见:https://github.com/bluemind7788/myknockout/tree/master/1.0.1

FED 团队成员需要具备的几个特质

FED 团队成员需要具备的几个特质

1. 良好的沟通能力

只顾于埋头苦干的人,在任何时候都会处于被动。

  • 有效的沟通,可以减少很多不必要的工作,从而提高工作效率;
  • 和不同的职位或层次的人沟通,可以扩宽你对当前工作的视野和认识;
  • 跟比你能力强的人进行深度沟通,胜过一万行代码。

小故事:合适的才是最好的

用友建筑的同事邀请我去进行React相关的技术培训,这套内容相对还是比较熟悉的,简单的准备了一下,便去进行了分享。

开头还不错,对技术的介绍、使用、分析、技巧等,都一一详细的说明。但是时间过去一个小时,发现效果不是很好,因为互动很少,发现大家对前端的技术认识太浅。

最后的两个小时,竟然是在和大家普及React和jQuery的**差异以及如何衔接上,而这些的讨论,却是对他们帮助最大的,因为大家还没有接触到太多太新的技术。

而这次的沟通是有价值的,对他们而言,通过讨论和交流,解决了很多基础认识层面上的疑惑;对我而言,知道后续应该如何在这个技术上的推广做一些努力。

不要闷头写代码

遇到问题,身边的小伙伴是你最得力的帮手。公司内外,都有值得你请教的人。不要当一个安静的coder,多一些讨论和交流。

2. 独立思考

我们的职位后面有三个字 ---- “工程师”,这是我们不同于其他职业的区别。

思考也是一种工作过程,经过思考后再进行工作效率会更高。见过很多人包括我自己在工作初期,也会出现这样的状况:

  • 拿到设计稿就开始写页面,后面发现总体结构需要调整,很多公用的代码还得重新梳理,这个过程导致工作量成倍增加
  • 接到任务马上开始写代码,而没有一个整体的思考过程,越写到后面越烦躁,思路越来越混乱
  • 会很自我意识的使用现有意识内掌握的方法去处理需求,导致的问题是,有可能一个简单正则或是一个算法就可以处理的问题,需要很复杂的代码甚至是多层嵌套循环才可以完成。

以上,是几个典型的状况。所以,为什么不在一开始的时候,多一点独立思考和沟通的过程呢,这将会是一个提高工作效率的好习惯。

3. 总结和分享

一个优秀的技术人才,都很擅长总结和表述自己的思路,这几乎是一个标配。公众场合的技术分享,有的人总能将一个复杂的应用娓娓道来,这是一种能力,但不是一个秘密,只要你愿意,你也能变成这样的优秀技术人才。

而他们,有一个共同的特点 ---- 喜欢总结,乐于分享。对每一个技术点日积月累,用自己所理解的方式沉淀下来,并且讲给别人听,让身边的人能听懂。长此以往,你会发现,你在慢慢的改变,变得越来越好。会喜欢研究技术,思路会变得更清晰,身边的人会很认可你,会喜欢和你交流。

你看啊,改变就是从这一点一滴的积累开始的。

4. 持续的学习

学习,是我们一辈子的事情。工作中的学习和学生时代的学习不一样,以前是学习课本知识,疲于应付考试;现在是学习是为了更好更多的掌握专业技能,为了升职加薪,为了自己的奥迪老婆的迪奥孩子的奥利奥,目的性更强了。

在工作之余,把更多的时间用在对前端技术的研究上,甚至是全栈或是全端。去Github找资料,去看官网的API文档,去买书看,看高质量的社区文章,偶尔的个人博客也可以关注。

但不可跟风学习,不可不学习,不可只用百度搜索学习,不可粘贴复制的学习。

作为工程师的我们,总该自己的一些坚持吧,而持续的学习,是最值得坚持的。

总结

请干了这碗鸡汤,然后,把这些话忘了。

植入到你的脑海中,并且践行。

gulp文件复制说起

gulp文件复制说起

在项目中,实现文件复制,常用的方法是使用gulp配置

var gulp = require('gulp');
vat path = ['./src/**'];

gulp.task('copy', function() {
  return gulp.src(path)
    .pipe(gulp.dest('./dist'));
})

开发效率来说,gulp是前端的利器,但究其源头,也是通过node来实现的,那就以node复制文件为实例进行说明,代码如下:

var fs = require( 'fs' ),
    stat = fs.stat;

/*
 * 复制目录中的所有文件包括子目录
 * @param{ String } 需要复制的目录
 * @param{ String } 复制到指定的目录
 */
var copy = function( src, dst ){
    // 读取目录中的所有文件/目录
    fs.readdir( src, function( err, paths ){
        if( err ){
            throw err;
        }
        paths.forEach(function( path ){
            var _src = src + '/' + path,
                _dst = dst + '/' + path,
                readable, writable;       
            stat( _src, function( err, st ){
                if( err ){
                    throw err;
                }
                // 判断是否为文件
                if( st.isFile() ){
                    // 创建读取流
                    readable = fs.createReadStream( _src );
                    // 创建写入流
                    writable = fs.createWriteStream( _dst );   
                    // 通过管道来传输流
                    readable.pipe( writable );
                }
                // 如果是目录则递归调用自身
                else if( st.isDirectory() ){
                    exists( _src, _dst, copy );
                }
            });
        });
    });
};
// 在复制目录前需要判断该目录是否存在,不存在需要先创建目录
var exists = function( src, dst, callback ){
    fs.exists( dst, function( exists ){
        // 已存在
        if( exists ){
            callback( src, dst );
        }
        // 不存在
        else{
            fs.mkdir( dst, function(){
                callback( src, dst );
            });
        }
    });
};
// 复制目录
exists( './src', './dist', copy );

注意,以上fs.readdir( src, callback )stat( _src, callback )是node中的异步回调。而在实际项目中,内容复制后需要进行编译,但以上方法因异步的执行顺序问题,无法进行编译。因此需要将以上两处转为同步执行。

异步写法:

fs.readdir( src, function(err, paths){
  // 内容执行;
} );

同步阻塞写法:

var paths = fs.readdirSync( src );
function(err, paths){
  // 内容执行;
}

异步是node的特色,从执行效率上,优先使用异步。

artTemplate在在官网中的应用

关于artTemplate的详细语法,请参考https://github.com/aui/artTemplate
下面详细介绍,artTemplate在官网项目中的一种用法。

引入artTemplate.js

//require引入artTemplate.js

require(['../lib/artTemplate'], function (template) {}

新增template模板,并引入

假设文件取名textTemplate.html


require(['../lib/artTemplate','text!../textTemplate.html'], function (template,textTemplate) {}

require引入artTemplate.js

textTemplate.html文件为modal与html的组合。例如

{{each list as value i}}
        <li>
            <div class="templateListMain">
                <div class="templateLisHead">
                    <h4 class="templateListTitle">
                        <a href="#">{{value.title}}</a>
                    </h4>
                </div>
                <div class="templateInfo clearfix">
                    <div class="templateInfoBox">
                        <ul class="templateInfoList">
                            <li class="left">模板编号:</li>
                            <li class="right">{{value.id}}</li>
                        </ul>
                    </div>
                </div>
                <div class="templateSpeInfo clearfix">
                    <span>{{value.decription}}</span>
                </div>
            </div>
        </div>
    </li>
{{/each}}

each循环变量list,value为每次循环的当前数据。list数据的来源后面做介绍

编译template模板

/**
 * 利用templat compile机制编译模板 并且插入参数{list:domList},list为数据别名,供   textTemplate.html 用
 * @domList 数据源
 * #templateContainer为页面的某个dom容器
 */
function initPate(){
    ar domList = [
            {
                'title': '金融理财模板',
                "id": "0004",
                'decription': '深耕银行、券商等金融机构的高净值客户,致力于打造专业的媒体管理模板',
                'content_img': "img/main_6.png",
                'phone_img': "img/phone_3.jpg",
                'url': 'http://design.yyuap.com/ficloud/home/statistics',
                "using_times": "228",
                "for_area": ["互联网",'金融']
            },{
                'title': '运维管理模板',
                "id": "005",
                'decription': '发展可持续、完善易有序、拓展能稳定',
                'content_img': "img/main_5.png",
                'phone_img': "img/phone_3.jpg",
                'url': '../../website/tenxcloud/index.html',
                "using_times": "228",
                "for_area": ["互联网",'金融']
            }
    ]
    var render = template.compile(textTemplate);
    var html = render({
        list: domList··
    });
    document.getElementById('templateContainer').innerHTML = html;
}

gitbook插件开发

gitbook插件开发

本次主要记录开发gitbook插件的一些记录,后续会根据开发进度不断完善。以下文档内容可参考:
https://github.com/iuap-design/gitbook-plugin-iuap-design

创建插件

1.创建仓库名字以gitbook-plugin-开头,后面为插件的具体名称
例如:gitbook-plugin-iuap-design
2.再package.json中需要添加

"engines": {
    "gitbook": ">1.x.x"
},

之后gitbook才能识别此插件

插件核心文件说明

index.js

module.exports = {
    book: {
        assets: './book',
        js: [
            'iuap-desigin.js'
        ],
        css: [
            'iuap-desigin.css'
        ]
    },
    hooks: {    
        "page": function (page) {             
            _.forEach(page.sections, insertAnchors);
            return page;
        }
    }
};

book下配置内容会出现在最终的产出内容中,并且在浏览页面时才执行。
hooks下配置hook,此例中的page可以获取gitbook插件生成的默认section标签中的内容,同时对此部分内容进行编辑修改,影响最终产出的html文件。

package.json

插件的配置信息。详细说明参考:
http://blog.csdn.net/zhangjk1993/article/details/50380403

插件使用

在book.json中的pulgins属性中添加创建的插件
例如:

"plugins":[
    "iuap-design"
]

插件调试

book内容调试

在book.json中添加插件执行执行

$ gitbook serve

或者

$ gitbook build

gitbook插件会根据book.json中的ouput产出html文件。进入此文件夹通过以下路径

gitbook\plugins\gitbook-plugin-iuap-design

可找到插件index.js中module.exports下book中配置的js及css,修改js及css刷新页面即可看到效果。

hooks内容调试

此部分内容只能通过重新执行

$ gitbook serve

或者

$ gitbook build

来进行调试。

以上内容只是目前开发过程的一些总结,有错误的地方欢迎提出来。

对日常开发的两点建议

分析和思考的过程比写代码更重要

大家拿到需求或是工作任务的时候,不要着急去做,先分析一下。自己要有一个思考的过程,整理清楚了再开始写代码。

比如这次的迁移开发过程,就需要对之前的代码进行改造和升级。同时,也需要对之前的样式和展示进行改造,不要被现有的东西束缚,存在的即是合理,但不代表存在的就是合适

  1. 维护性或是配置性的代码要单独抽离维护
  2. 大量拼接html片段的使用handlebars underscore 等进行模板抽离
  3. 保持代码逻辑清晰

保持疑问

我们做的工作是提供公共的资源,从这一点考虑,就需要严格要求。对看到不合理的逻辑要指出,对感觉不合适的组织方式要提出,对存在的东西都保持批判。你可以认为我们现在做的东西都特别垃圾,那么接下来思考如何优化

自己动手写Knockoutjs - 实现基本的双向数据绑定

了解一个开源的框架可以到网上看教程,要达到将框架运用到炉火纯青的地步就必须要了解其本身的原理,了解原理最好的方式莫过于自己亲手来实现一个简化的版本。
这边博客就教大家如何实现一个简化版本的Knockout, Knockout的源码现在已经有五千多行,这个版本的ko仅有七十行,实现了基本的双向数据绑定、两个绑定(value、text)。

1. 实现ko.observable

ko对象是一个function对象

ko.observable = function (initialValue) {
    function observable(newValue) {
    }
    return observable;
}

要想知道数据发生了变化,必须要保存之前的数据,在这里可以用闭包保存老数据。

ko.observable = function (initialValue) {
var _latestValue = initialValue;
function observable(newValue) {
        if (arguments.length > 0) {
            _latestValue = newValue;
            observable.notifySubscribers(_latestValue);
        }
        return _latestValue;
    }
    return observable;
}

在ko中对于数据的set和get方法,可以通过参数的多少来判断,当有参数传入时,即是set方法。
数据变化的监听用观察者模式实现。

ko.subscribable = function () {
    var _subscriptions = [];

    this.subscribe = function (callback) {
        _subscriptions.push(callback);
    };

    this.notifySubscribers = function (valueToNotify) {
        for(var i = 0; i < _subscriptions.length;i++) {
            _subscriptions[i](valueToNotify);
        }
    };
}

ko.observablecall来继承ko.subscribable的属性,ko.subscribable.call(observable);

2. 自定义绑定的实现

自定义绑定要定义两件事情:数据变化时操作dom来改变视图;监听dom的变化,定义变化后去改变数据的方法。

ko.bindingHandlers.value = {
    init: function(element, value) {
        element.addEventListener("change", function () { value(this.value); }, false);
    },
    update: function (element, value) {
        element.value = value();
    }
};

3. ko.applyBindings 的实现

ko.applyBindings 是view和model 绑定的入口。它需要1. 解析html代码中的绑定 2.执行自定义绑定中的init 3. 给model注册监听,监听的回调要做的事情是调用自定义绑定的update方法。
这里仅实现了单个dom node和model的绑定。

ko.applyBindingsToNode = function (viewModel, node) {
    var isFirstEvaluation = true;
    function evaluate() {
        var parsedBindings = parseBindingAttribute(node.getAttribute(bindingAttributeName), viewModel);
        for (var bindingKey in parsedBindings) {
            if (ko.bindingHandlers[bindingKey]) {
                if (isFirstEvaluation && typeof ko.bindingHandlers[bindingKey].init == "function") {
                    ko.bindingHandlers[bindingKey].init(node, parsedBindings[bindingKey]);
                    parsedBindings[bindingKey].subscribe(evaluate);
                }
                if (typeof ko.bindingHandlers[bindingKey].update == "function") {
                    ko.bindingHandlers[bindingKey].update(node, parsedBindings[bindingKey]);
                }
                if (isFirstEvaluation) parsedBindings[bindingKey].subscribe(evaluate);
            }
        }
    }

    evaluate();
    isFirstEvaluation = false;
};

完整的代码和测试例子见https://github.com/bluemind7788/myknockout/tree/master/1.0.0

自己动手写Knockoutjs - 判断observable对象的方法和更多的绑定

自己动手写Knockoutjs - 判断observable对象的方法和更多的绑定

1. 测试用例

https://github.com/bluemind7788/myknockout/blob/master/1.0.3/test.html

2. 判断一个对象是否是observable、dependentObservable

observable都是function对象,通过typeof、instanceof并不能判断出来。
dependentObservable也是observable,需要判断为true。
参考javascript的原型链,我们为observable添加一个ko_proto的属性,它来标识一个observable对象,它指向ko.observable。ko.observable也加一个ko_proto的属性,它指向它的父。

observable添加

observable.__ko_proto__ = ko.observable;
}

dependentObservable添加

dependentObservable.__ko_proto__ = ko.dependentObservable;
}
ko.dependentObservable.__ko_proto__ = ko.observable;

判断observable的方法

ko.isObservable = function(instance) {
    if(instance == null || instance == undefined || instance.__ko_proto__ == undefined) return false;
    if(instance.__ko_proto__ == ko.observable) return true;
    return ko.isObservable(instance.__ko_proto__);
}

3. click绑定

ko.bindingHandlers.click = {
    init: function(element, value, viewModel) {
        ko.utils.registerEventHandler(element, 'click', function(event) {
            try{
                value.call(viewModel);
            } finally {
                if(event.preventDefault) {
                    event.preventDefault();
                } else {
                    event.returnValue = false;
                }
            }
        })
    }
};

registerEventHandler是一个通用化的方法,我们放到utils里面。

registerEventHandler: function(element, eventType, handler) {
    if(element.addEventListener) {
        element.addEventListener(eventType, handler, false);
    } else if(element.attachEvent) {
        element.addEventListener('on' + eventType, function(event) {
            handler.call(element, event);
        });
    }
}

4. css绑定

ko.utils.css = {
    update: function(element, value, viewModel) {
        value = value || {};
        for(var className in value) {
            var shouldHasClass = value[className];
            ko.utils.toggleDomNodeCssClass(element, className, shouldHaveClass);

        }
    }
}

toggleDomNodeCssClass需要用类似于jquery操作样式常用的方法hasClass、addClass、removeClass等方法,我们都把他们加到utils里面,具体看源码。

toggleDomNodeCssClass : function(element, className, shouldHaveClass) {
    var hasClass = ko.utils.hasClass(element, className);
    if(shouldHaveClass && !hasClass) {
        ko.utils.addClass(element, className)
    } else if(!shouldHaveClass && hasClass){
        ko.utils.removeClass(element, className)
    }
}

具体的实现和例子,参照https://github.com/bluemind7788/myknockout/tree/master/1.0.3

nodejs之process模块

nodejs之process模块

process可以用于node和系统中已经存在的进程进行交互,创建工作子进程等。process模块是一个全局对象,允许你获得或者修改当前node进程的设置。

1.process的引入

process模块用来与当前进程互动,可以通过全局变量process访问,不必使用require命令加载。它是一个EventEmitter对象的实例。

2.process事件

1)exit事件

当前进程退出时(即按"ctre+c"时)会触发exit事件,可以对该事件指定回调函数。这是一个用来定时检测模块状态的钩子。当主事件循环在执行完exit的回调函数后将不再执行,所以在exit事件中定义的定时器不会被加入到事件列表

process.on('exit', function() {
  // 设置一个延迟执行
  setTimeout(function() {
console.log('主事件循环已停止,所以不会执行');
  }, 0);
  console.log('退出前执行');
});

2)uncaughtException事件

这是process的异常事件,uncaughtException译为:未捕获的异常,可以利用这个函数来捕获整个进程运行时的异常,这里可以理解为“使本node.js进程中断的异常”

process.on('uncaughtException', function (err) {
  console.log('Caught exception: ' + err);
});
nonexistentFunc();
console.log('This will not run.');

说明:以上的例子注册了uncaughtException事件来捕获系统异常,执行到nonexistentFunc()时,因为改函数没有定义所以会抛出异常。因为javascript是解释性语言,nonexistentFunc()方法上面的语句不会被影响到,他下面的语句不会执行。所以他执行的结果如下:

Caught exception: ReferenceError: nonexistentFunc is not defined
This will still run.

2.process属性

  • process.pid:当前进程的进程号。
  • process.version:Node的版本,比如v0.10.18。
  • process.platform:当前系统平台,比如Linux。
  • process.title:默认值为“node”,可以自定义该值。
  • process.argv:当前进程的命令行参数数组。
  • process.env:指向当前shell的环境变量。
  • process.execPath:运行当前进程的可执行文件的绝对路径。
  • process.stdout:指向标准输出。
  • process.stdin:指向标准输入。
  • process.stderr:指向标准错误。

以下是主要属性的介绍:

1)stdout

process.stdout用来控制标准输出,也就是在命令行窗口向用户显示内容。它的write方法等同于console.log。

process.stdout.write('description:');

2)argv

process.argv返回命令行脚本的各个参数组成的数组。数组第一项是node,第二项是.js文件的名称,接下来依次是命令行传入的参数
建立iweb.js

console.log(process.argv);

在命令行输入:

node iweb.js 3000 2000

结果如下:

[ 'C:\\Program Files\\nodejs\\node.exe','D:\\portal\\iuap_portal_fe\\node_modules\\iweb\\bin\\iweb.js','3000','2000' ]

3.process方法

  • process.abort():退出node并创建一个核心文件
  • process.exit():退出当前进程。
  • process.cwd():返回运行当前脚本的工作目录的路径。
  • process.chdir():改变工作目录。
  • process.nextTick():将一个回调函数放在下次事件循环的顶部。
  • process.kill():向进程发送一个信号
  • process.memoryUsage():返回内存使用情况单位是bytes。
  • process.uptime():返回 Node 程序已运行的秒数。
  • process.hrtime():

以下是部分方法的介绍:

1)process.chdir()

改变进程的当前进程的工作目录,若操作失败则抛出异常。

console.log('当前目录:' + process.cwd());
try {
  process.chdir('/tmp');
  console.log('新目录:' + process.cwd());
}
catch (err) {
  console.log('chdir: ' + err);
}

2)process.nextTick(callback)

在事件循环的下一次循环中调用 callback 回调函数。

console.log('开始');
process.nextTick(function() {
  console.log('nextTick 回调');
});
console.log('已设定');

结果如下:

 输出:
 开始
 已设定
 nextTick 回调

3)process.kill(pid, [signal])

向进程发送一个信号。 pid 是进程的 id 而 signal 则是描述信号的字符串名称。信号的名称都形似 'SIGINT' 或者 'SIGUSR1'。如果没有指定参数则会默认发送 'SIGTERM' 信号 。

process.kill(process.pid, 'SIGHUP'); 

自己动手写Knockoutjs - 可监控数组

自己动手写Knockoutjs - 可监控数组

1. 测试用例

<select id="select1" multiple="multiple" style="width: 200px" data-bind="options:items"></select>
<button onclick="addItem()">添加</button>
<button onclick="delLastItem()">删除最后一项</button>
<button onclick="reverse()">反转</button>

<script src="knockout.js"></script>
<script>
    var myViewModel = { };
       myViewModel.items = ko.observableArray(["Alpha", "Beta", "Gamma"])
    ko.applyBindingsToNode(myViewModel, document.getElementById('select1'));
    function addItem() {
        myViewModel.items.push('Added');
    }
    function delLastItem() {
        myViewModel.items.pop();
    }
    function reverse() {
        myViewModel.items.reverse();
    }
</script>

2. 可监控数组的定义

不同于一般的observable对象,可监控数组需要有更多的操作api,如push、pop等。这样,我们可以通过继承observable,然后再加入扩展的方法。

ko.observableArray = function(initialValues) {
    var result = new ko.observable(initialValues);
    return result;
}

3. 扩展方法的实现

通过result()可以得到原生的数组对象,在扩展方法里需要做两件事:操作原生数组;通知subscribtiones。

["pop", "push", "reverse", "shift", "sort", "splice", "unshift"].forEach(function(methodName) {
    result[methodName] = function() {
        var underlyingArray = result();
        var methodCallResult = underlyingArray[methodName].apply(underlyingArray, arguments);
        result.valueHasMutated();
        return methodCallResult;
    }
});

valueHasMutated的实现放在observable中。

observable.valueHasMutated = function () { 
    observable.notifySubscribers(_latestValue); 
}

对于像slice这样的方法,当其被调用时是不需要发出通知的。

['slice'].forEach(function(methodName) {
    result[methodName] = function() {
            var underlyingArray = result();
               return underlyingArray[methodName].apply(underlyingArray, arguments);
        }
});

具体的实现和例子,参照https://github.com/bluemind7788/myknockout/tree/master/1.0.2

iuap design 源码分析系列-双MVVM模型

分析1 何谓双MVVM

现在已经有很多流行的MVVM框架,如knockout、vue、angular、avalon等,它们都实现了普通的html元素与简单数据(包括基本数据类型和数组)的双向绑定。但是在View层,除了普通的html元素,还有很多js插件,它们是在js代码中通过拼接html的方式渲染出来的前端展示,如很多的jquery插件;在Model层,简单的数据在一些场景下会有些力不从心,如knockout的数组,改变数组的一行中某列的值,View层并不能更新;列的元信息如字段名、校验规则等并不能在数组结构中表示;数组数据的选中行等统计信息,只能在数组外添加计算属性实现。
双MVVM除了实现普通html元素与简单数据绑定外,还实现了普通html元素与复杂数据、jsView插件与简单数据、jsView插件与复杂数据的绑定。

分析2 双MVVM的实现原理

普通html与简单数据、复杂数据的绑定通过knockout就可以搞定,这是我们的第一种绑定方式。
jsView插件与简单数据、复杂数据的绑定是需要针对每一个jsView插件加入一个中介者,这个中介者实现了数据的监听和控件的监听,当数据发生变化时,中介者会去调用jsView插件提供的api去更新view,当控件发生变化时,会调用复杂数据的api更新数据,这是我们的第二种绑定的实现的基本原理。
两种绑定方式的区别绑定view的粒度不同,一个是html元素级别,一个控件级别。在iuap design中,主要是实现了第二种绑定方式。

分析3 举例说明第二种绑定方式

这种绑定方式的代码在src/model/comp-adp目录下,我们以checkbox.js来分析。先看看简化版的代码

u.CheckboxAdapter = u.BaseAdapter.extend({
    init: function (options) {
        var self = this;
        // 初始化控件
        this.comp = new u.Checkbox(this.element);
        this.element['u.Checkbox'] = this.comp; 
        // 监听控件的变化
        this.comp.on('change', function(){
            var modelValue = self.dataModel.getValue(self.field);
            modelValue = modelValue ? modelValue : '';

            if (self.comp._inputElement.checked) {
                self.dataModel.setValue(self.field, self.checkedValue);
            }else{
                self.dataModel.setValue(self.field, self.unCheckedValue)
            }
        });

        if(this.dataModel){
            // 监听datatable的变化
            this.dataModel.ref(this.field).subscribe(function(value) {
                self.modelValueChange(value)
            })
        }
    },
    modelValueChange: function (val) {
        var self = this;
        if (this.comp._inputElement.checked != (val === this.checkedValue)){
            this.comp.toggle();
        }
    }
})

可以看到通过new u.Checkbox(this.element)进行了控件的初始化;this.comp.on('change', function(){...})实现了控件的监听,监听的回调方法中通过self.dataModel.setValue(self.field, self.checkedValue)进行了复杂数据datatable的改变; this.dataModel.ref(this.field).subscribe(function(value) {
self.modelValueChange(value)
})实现了复杂数据的监听,在监听函数modelValueChange中通过this.comp.toggle()`对控件进行了更新。

未完待续

框架css梳理

框架css

1.基础样式

  • 重置样式 normalize.css
  • 字号、单位、盒模型等基础设置
  • 字体
  • 文字排版(h1-h6 p hr pre blockquote ul ol table img)

2.布局

  • 栅格系统

3.辅助类

  • 布局相关的
  • 文本展示相关的
  • 响应式辅助的

4.组件

基础组件

  • 图标字体icon
  • 按钮button
  • 代码code
  • 表单table
  • 图片image
  • 表格grid

UI组件

  • 徽章badge
  • 面包屑导航breadcrumb
  • 按钮组button-goup
  • 关闭按钮close
  • 评论列表comment
  • 图标字体icon
  • 输入框组input-group
  • 列表 list
  • 导航nav
  • 导航条navbar
  • 分页pagination
  • 面板panel
  • 进度条progress
  • 缩略图thumbnail
  • css动画animation
  • 文章页article

使用forever管理我们的app

About

forever能够启动,停止,重启应用

forever可以看做是一个nodejs的守护进程,使用forever启动的app在退出ssh终端之后不会结束

常用命令

  • forever start /data/github/githook

可选参数 -w 监听文件改动并自动重启服务.

  • forever stop /data/github/githook 或 任务id.
  • forever list 显示所有运行的服务
    从结果中可以看到任务的id, 任务的状态,日志文件位置,任务执行时间.

注意事项

  • forever start /data/github/githook 和forever start /data/github/githook/ 都能启动githook,然而forever会认为他们是两个不同的app.建议启动的时候不要加末尾的/
  • 可以根据list命令中的日志文件位置来找到日志并查看.也可以在start的时候通过参数 -l指定日志文件的位置.
  • 目前的任务列表
    data: uid command script forever pid id logfile uptime
    data: [0] 6v-- /usr/local/node-v6.2.1-linux-x64/bin/node /data/github/iuapfed/generate-uui 21225 21231 /root/.forever/6v--.log STOPPED
    data: [1] pQFa /usr/local/node-v6.2.1-linux-x64/bin/node /data/github/iuapfed/iuap-design.github.io 21247 9708 /root/.forever/pQFa.log 0:2:29:23.385
    data: [2] Or4L /usr/local/node-v6.2.1-linux-x64/bin/node /data/github/githook/ 495 511 /root/.forever/Or4L.log 0:0:30:55.327

Node FileSystem

Node FileSystem

Node为所有API实现了同步Synchronous和异步Asynchronous,性能上得到了极大的优化。

同步or异步

从网站处理并发来讲,异步为此而生。但就前段项目工程化的角度,只是用来实现页面静态化输出,同步的性能要优于异步,毕竟在整体执行时间上有一点小优势,且能跳过不少异步的坑。

// vs.js
var fs = require("fs");

// Asynchronous read
fs.readFile('vs.txt', function (err, data) {
   if (err) {
       return console.error(err);
   }
   console.log("Asynchronous read: " + data.toString());
});

// Synchronous read
var data = fs.readFileSync('vs.txt');
console.log("Synchronous read: " + data.toString());

console.log("Program Ended");

vs.txt内容

Synchronous + Asynchronous Content

执行node vs结果

192:vs liwei$ node vs
Synchronous read: Synchronous + Asynchronous Content
Program Ended
Asynchronous read: Synchronous + Asynchronous Content

本次主要通过一些小实例,展示Node fs一些常用的API.
Node的API接口提供了简单的复制和粘贴功能.

利用pipe管道实现复制:

var fs = require('fs');
var readable = fs.createReadStream('./original.txt');
var writeable = fs.createWriteStream('./copy.txt');
readable.pipe(writeable);

Node也提供了批量输出Bulk file I/O的API,下例实现内容存储在缓存中:

var fs = require('fs');
fs.readFile('./copy.txt', function(err, buf) {
    console.log(buf.toString());//输出缓存内容
});

利用fs.watch监视文件变化:

var fs = require('fs');
fs.watch('./', function(event,filename) {
    console.log('event is:' + event); // 监视的类型:'rename','change' 
    if(filename) {
        console.log('filename provided:' + filename);
    } else {
        console.log('filename not provided');
    }
})

根据官方文档fs.watch,目前watch不一定适用所有平台:

The fs.watch API is not 100% consistent across platforms, and is unavailable in some situations. The recursive option is only supported on OS X and Windows.

经测试watch只能实现renamechange事件。文件增删时,提示均为rename

监视文件变化的另外一个API为:fs.watchFile

查找文件

var fs = require('fs');
var join = require('path').join;

exports.findSync = function (nameRe, startPath) {
    var results = [];

    function finder(path) {
        var files = fs.readdirSync(path);

        for(var i=0; i<files.length; i++) {
            var fpath = join(path, files[i]);
            var stats = fs.statSync(fpath);

            if(stats.isDirectory()) finder(fpath);
            if(stats.isFile() && nameRe.test(files[i])) results.push(fpath);
        }
    }

    finder(startPath);
    return results;
}

在查找资料过程中,可以选择使用一些异步库sdync简化代码。

简单示例地址

参考:

node-js-in-practice

javascript 代码规范

javascript 代码规范

团队代码规范-- js代码规范,用于规范团队内统一的代码风格。

1、缩进

缩进的单位为四个空格。

2、每行长度

避免每行超过80个字符。当一条语句一行写不下时,请考虑折行。

3、注释

  • 不要吝啬注释。注释应该和它们所注释的代码一样是书写良好且清晰明了。
  • 及时地更新注释也很重要。错误的注释会让程序更加难以阅读和理解。
  • 让注释有意义。重点在解释那些不容易立即明白的逻辑上。不要把读者的时间浪费在阅读类似于:
    i = 0; //让i等于0 使用单行注释。

4、变量声明

  • 所有的变量必须在使用前进行声明。JavaScript并不强制必须这么做,但是这么做可以让程序易于阅读,且也容易发现那些没声明的变量(它们会被编译成全局变量)。

  • 将var语句放在函数的首部。

  • 最好把每个变量的声明语句单独放到一行,并加上注释说明。所有变量按照字母排序。如下

    var currentEntry; // 当前选择项
    var level;// 缩进程度
    var size; // 表格大小
    
  • 尽量减少全局变量的使用。不要让局部变量覆盖全局变量。

5、字符串

统一使用单引号(‘),不使用双引号(“)。这在创建 HTML 字符串非常有好处:

var msg = 'This is some HTML <div class="makes-sense"></div>';

6、函数声明

  • 所有的函数在使用前进行声明。 内函数的声明跟在var语句的后面。这样可以帮助判断哪些变量是在函数范围内的。

    function outer(c, d) {
        var e = c * d;
    
        function inner(a, b) {
        return (e * a) + b;
        }
    
        return inner(0, 1);
    }
    
  • 如果函数是匿名函数,则在function和((左括号)之间应有一个空格。

    div.onclick = function (e) {
        return false;
    };
    
  • 尽量不使用全局函数。

7、命名

  • 变量名应由26个大小写字母(A..Z,a..z),10个数字(0..9),和 _(下划线)组成。
  • 不要把 _(下划线)作为变量名的第一个字符。
  • 大多数的变量名和方法命应以小写字母开头。
  • 必须与new共同使用的构造函数名应以大写字母开头。
  • 全局变量应该全部大写。

8、语句

8.1、简单语句

每一行最多只包含一条语句。把;(分号)放到每条简单语句的结尾处。

8.2、复合语句

  • 复合语句是被包含在{ }(大括号)的语句序列。
  • 被括起的语句必须多缩进四个空格。
  • {(左大括号)应在复合语句起始行的结尾处。
  • }(右大括号)应与{(左大括号)的那一行的开头对齐
  • 大括号应该在所有复合语句中使用,即使只有一条语句,当它们是控制结构的一部分时, 比如一个if或者for语句。这样做可以避免以后添加语句时造成的错误。

8.3、严格模式

ECMAScript 5 严格模式可在整个脚本或独个方法内被激活。它对应不同的 javascript 语境会做更加严格的错误检查。严格模式也确保了 javascript 代码更加的健壮,运行的也更加快速。

不推荐

// Script starts here
'use strict';

(function(){

  // Your code starts here

}());

推荐

(function(){
  'use strict';

  // Your code starts here

}());

8.4、变量赋值时的逻辑操作

逻辑操作符 || 和 && 也可被用来返回布尔值。如果操作对象为非布尔对象,那每个表达式将会被自左向右地做真假判断。基于此操作,最终总有一个表达式被返回回来。这在变量赋值时,是可以用来简化你的代码的。

不推荐

if(!x) {
  if(!y) {
x = 1;
  } else {
x = y;
  }
}

推荐

x = x || y || 1;

8.5、return 语句

一条有返回值的return语句不要使用( )(括号)来括住返回值。如果返回表达式,则表达式应与return 关键字在同一行,以避免误加分号错误。

8.6、if 语句

if语句应如以下格式:

if (condition){
    statements;
}

if (condition) {
    statements;
} else {
    statements;
}

if (condition) {
    statements;
} else if (condition) {
    statements;
} else {
    statements;
}

8.7、for 语句

for语句应如以下格式:

for (initialization;condition; update) {
    statements;
}

for (variable in object){
    if (filter) {
        statements;
    }
}

第一种形式的循环用于已经知道相关参数的数组循环。
第二种形式应用于对象中。object原型中的成员将会被包含在迭代器中。通过预先定义hasOwnProperty方法来区分真正的object成员是个不错方法:

for (variablein object) {
    if (object.hasOwnProperty(variable)){
        statements;
    }
}

8.8、while 语句

while语句应如以下格式:

while (condition){
    statements;
}

9、do 语句

do语句应如以下格式:

do {
    statements;
} while (condition);

注:不像别的复合语句,do语句总是以;(分号)结尾。

10、switch 语句

switch语句应如以下格式:

switch (expression){
case expression:
    statements;
default:
    statements;
}

每个 case与switch对齐。这可避免过分缩进。
每一组statements(除了default应以 break,return,或者throw结尾。不要让它顺次往下执行。

8.11、try 语句

try语句应如以下格式:

try {
    statements;
} catch (variable){
    statements;
}

try {
    statements;
} catch (variable){
    statements;
} finally {
    statements;
}

8.12、continue 语句

避免使用continue语句。它很容易使得程序的逻辑过程晦涩难懂。

8.13、with 语句

不要使用with语句。

9、另外的建议

9.1、{} 和[]

  • 使用{}代替new Object()。使用[]代替new Array()。
  • 当成员名是一组有序的数字时使用数组来保存数据。当成员名是无规律的字符串或其他时使用对象来保存数据。

9.2、,(逗号)操作符

避免使用逗号操作符,除非在特定的for 语句的控制部分。(这不包括那些被用在对象定义,数组定义,var语句,和参数列表中的逗号分隔符。)

9.3、作用域

在JavaScript中块没有域。只有函数有域。不要使用块,除非在复合语句中。

9.4、赋值表达式

避免在if和while语句的条件部分进行赋值。

9.5、===和!==操作符。

使用===和!==操作符会相对好点。==和!=操作符会进行类型强制转换。 特别是, 不要将==用于与错值比较( false,null,undefined,“”,0,NaN)。

9.6、令人迷惑的加号和减号

不要在+后紧跟+或++。这种形式很容易仍人迷惑。应插入括号以便于理解。

total = subtotal + +myInput.value;

最好能写成

total = subtotal + (+myInput.value);

这样+ +不会被误认为是++。

9.7、避免使用eval

eval是JavaScript中最容易被滥用的方法。避免使用它。

9.8、规范定义JSON对象,补全双引号

9.9、不在文件中留下未来确定不再使用的代码片段

requirejs简单应用

#1.requirjs的引用

<script src='http://design.yyuap.com/static/requirejs/require.js' data-main='main'></script>
为了防止页面失去响应,可以将这个script标签写到页面的最下边,或者写成如下
<script src="http://design.yyuap.com/static/requirejs/require.js" data-main='main'></script>
说明:data-main='入口文件文件路径'。requirejs默认的文件名是.js,所以可以省略.js后缀
#2.主模块,入口文件main.js的写法

require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){});
参数说明:第一个参数是一个数组,表示依赖的模块。第二个参数是一个回调函数,当前边指定的模块被加载完成时候,这个回调函数被调用,加载的模块会传进这个回调函数。
还可以使用require.config()对模块加载的行为进行定义,require.config()写在main.js开头。

 require.config({
        paths:{
            'moduleA':'moduleA的路径',
            'moduleB':'moduleB的路径'
                }
        })

上边的写法,moduleA和moduleB都和main.js在同一个文件夹下,如果不在同一个文件夹下,可以指定在require.config()中设置baseUrl。
#3.自定义模块的写法:

define(id, dependencies, factory);
参数说明:id:可选参数,用来定义模块的标识,如果没有提供该参数,为文件名(去掉拓展名);
dependencies:是一个当前模块依赖的模块名称数组
factory:工厂方法,模块初始化要执行的函数或对象。如果为函数,它应该只被执行一次。如果是对象,此对象应该为模块的输出值
#4.使用实例

// 定义模块 myModule.js
define(['dependency'], function(){
var name = 'lsz';
function printName(){
    console.log(name);
}
return {
    printName: printName
};
});
// 加载模块
require(['myModule'], function (my){
    my.printName();
});

windows上jekyll安装指南

windows上jekyll安装指南

1. 安装ruby

到ruby官网或是百度一下,找个包安装即可

2. 安装python2.7版本

  • 下载安装,记得安装的时候几个添加环境变量的选项勾上
  • 配置环境变量,这样可以用easy_install命令
C:\Python27\Scripts 

3. 安装easy_install

在D盘新建ez_setup.py文件,以下是文件的代码

#!/usr/bin/env python

"""
Setuptools bootstrapping installer.

Maintained at https://github.com/pypa/setuptools/tree/bootstrap.

Run this script to install or upgrade setuptools.
"""

import os
import shutil
import sys
import tempfile
import zipfile
import optparse
import subprocess
import platform
import textwrap
import contextlib
import json
import codecs

from distutils import log

try:
    from urllib.request import urlopen
    from urllib.parse import urljoin
except ImportError:
    from urllib2 import urlopen
    from urlparse import urljoin

try:
    from site import USER_SITE
except ImportError:
    USER_SITE = None

LATEST = object()
DEFAULT_VERSION = LATEST
DEFAULT_URL = "https://pypi.io/packages/source/s/setuptools/"
DEFAULT_SAVE_DIR = os.curdir


def _python_cmd(*args):
    """
    Execute a command.

    Return True if the command succeeded.
    """
    args = (sys.executable,) + args
    return subprocess.call(args) == 0


def _install(archive_filename, install_args=()):
    """Install Setuptools."""
    with archive_context(archive_filename):
        # installing
        log.warn('Installing Setuptools')
        if not _python_cmd('setup.py', 'install', *install_args):
            log.warn('Something went wrong during the installation.')
            log.warn('See the error message above.')
            # exitcode will be 2
            return 2


def _build_egg(egg, archive_filename, to_dir):
    """Build Setuptools egg."""
    with archive_context(archive_filename):
        # building an egg
        log.warn('Building a Setuptools egg in %s', to_dir)
        _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
    # returning the result
    log.warn(egg)
    if not os.path.exists(egg):
        raise IOError('Could not build the egg.')


class ContextualZipFile(zipfile.ZipFile):

    """Supplement ZipFile class to support context manager for Python 2.6."""

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        self.close()

    def __new__(cls, *args, **kwargs):
        """Construct a ZipFile or ContextualZipFile as appropriate."""
        if hasattr(zipfile.ZipFile, '__exit__'):
            return zipfile.ZipFile(*args, **kwargs)
        return super(ContextualZipFile, cls).__new__(cls)


@contextlib.contextmanager
def archive_context(filename):
    """
    Unzip filename to a temporary directory, set to the cwd.

    The unzipped target is cleaned up after.
    """
    tmpdir = tempfile.mkdtemp()
    log.warn('Extracting in %s', tmpdir)
    old_wd = os.getcwd()
    try:
        os.chdir(tmpdir)
        with ContextualZipFile(filename) as archive:
            archive.extractall()

        # going in the directory
        subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
        os.chdir(subdir)
        log.warn('Now working in %s', subdir)
        yield

    finally:
        os.chdir(old_wd)
        shutil.rmtree(tmpdir)


def _do_download(version, download_base, to_dir, download_delay):
    """Download Setuptools."""
    egg = os.path.join(to_dir, 'setuptools-%s-py%d.%d.egg'
                       % (version, sys.version_info[0], sys.version_info[1]))
    if not os.path.exists(egg):
        archive = download_setuptools(version, download_base,
                                      to_dir, download_delay)
        _build_egg(egg, archive, to_dir)
    sys.path.insert(0, egg)

    # Remove previously-imported pkg_resources if present (see
    # https://bitbucket.org/pypa/setuptools/pull-request/7/ for details).
    if 'pkg_resources' in sys.modules:
        _unload_pkg_resources()

    import setuptools
    setuptools.bootstrap_install_from = egg


def use_setuptools(
        version=DEFAULT_VERSION, download_base=DEFAULT_URL,
        to_dir=DEFAULT_SAVE_DIR, download_delay=15):
    """
    Ensure that a setuptools version is installed.

    Return None. Raise SystemExit if the requested version
    or later cannot be installed.
    """
    version = _resolve_version(version)
    to_dir = os.path.abspath(to_dir)

    # prior to importing, capture the module state for
    # representative modules.
    rep_modules = 'pkg_resources', 'setuptools'
    imported = set(sys.modules).intersection(rep_modules)

    try:
        import pkg_resources
        pkg_resources.require("setuptools>=" + version)
        # a suitable version is already installed
        return
    except ImportError:
        # pkg_resources not available; setuptools is not installed; download
        pass
    except pkg_resources.DistributionNotFound:
        # no version of setuptools was found; allow download
        pass
    except pkg_resources.VersionConflict as VC_err:
        if imported:
            _conflict_bail(VC_err, version)

        # otherwise, unload pkg_resources to allow the downloaded version to
        #  take precedence.
        del pkg_resources
        _unload_pkg_resources()

    return _do_download(version, download_base, to_dir, download_delay)


def _conflict_bail(VC_err, version):
    """
    Setuptools was imported prior to invocation, so it is
    unsafe to unload it. Bail out.
    """
    conflict_tmpl = textwrap.dedent("""
        The required version of setuptools (>={version}) is not available,
        and can't be installed while this script is running. Please
        install a more recent version first, using
        'easy_install -U setuptools'.

        (Currently using {VC_err.args[0]!r})
        """)
    msg = conflict_tmpl.format(**locals())
    sys.stderr.write(msg)
    sys.exit(2)


def _unload_pkg_resources():
    sys.meta_path = [
        importer
        for importer in sys.meta_path
        if importer.__class__.__module__ != 'pkg_resources.extern'
    ]
    del_modules = [
        name for name in sys.modules
        if name.startswith('pkg_resources')
    ]
    for mod_name in del_modules:
        del sys.modules[mod_name]


def _clean_check(cmd, target):
    """
    Run the command to download target.

    If the command fails, clean up before re-raising the error.
    """
    try:
        subprocess.check_call(cmd)
    except subprocess.CalledProcessError:
        if os.access(target, os.F_OK):
            os.unlink(target)
        raise


def download_file_powershell(url, target):
    """
    Download the file at url to target using Powershell.

    Powershell will validate trust.
    Raise an exception if the command cannot complete.
    """
    target = os.path.abspath(target)
    ps_cmd = (
        "[System.Net.WebRequest]::DefaultWebProxy.Credentials = "
        "[System.Net.CredentialCache]::DefaultCredentials; "
        '(new-object System.Net.WebClient).DownloadFile("%(url)s", "%(target)s")'
        % locals()
    )
    cmd = [
        'powershell',
        '-Command',
        ps_cmd,
    ]
    _clean_check(cmd, target)


def has_powershell():
    """Determine if Powershell is available."""
    if platform.system() != 'Windows':
        return False
    cmd = ['powershell', '-Command', 'echo test']
    with open(os.path.devnull, 'wb') as devnull:
        try:
            subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
        except Exception:
            return False
    return True
download_file_powershell.viable = has_powershell


def download_file_curl(url, target):
    cmd = ['curl', url, '--location', '--silent', '--output', target]
    _clean_check(cmd, target)


def has_curl():
    cmd = ['curl', '--version']
    with open(os.path.devnull, 'wb') as devnull:
        try:
            subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
        except Exception:
            return False
    return True
download_file_curl.viable = has_curl


def download_file_wget(url, target):
    cmd = ['wget', url, '--quiet', '--output-document', target]
    _clean_check(cmd, target)


def has_wget():
    cmd = ['wget', '--version']
    with open(os.path.devnull, 'wb') as devnull:
        try:
            subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
        except Exception:
            return False
    return True
download_file_wget.viable = has_wget


def download_file_insecure(url, target):
    """Use Python to download the file, without connection authentication."""
    src = urlopen(url)
    try:
        # Read all the data in one block.
        data = src.read()
    finally:
        src.close()

    # Write all the data in one block to avoid creating a partial file.
    with open(target, "wb") as dst:
        dst.write(data)
download_file_insecure.viable = lambda: True


def get_best_downloader():
    downloaders = (
        download_file_powershell,
        download_file_curl,
        download_file_wget,
        download_file_insecure,
    )
    viable_downloaders = (dl for dl in downloaders if dl.viable())
    return next(viable_downloaders, None)


def download_setuptools(
        version=DEFAULT_VERSION, download_base=DEFAULT_URL,
        to_dir=DEFAULT_SAVE_DIR, delay=15,
        downloader_factory=get_best_downloader):
    """
    Download setuptools from a specified location and return its filename.

    `version` should be a valid setuptools version number that is available
    as an sdist for download under the `download_base` URL (which should end
    with a '/'). `to_dir` is the directory where the egg will be downloaded.
    `delay` is the number of seconds to pause before an actual download
    attempt.

    ``downloader_factory`` should be a function taking no arguments and
    returning a function for downloading a URL to a target.
    """
    version = _resolve_version(version)
    # making sure we use the absolute path
    to_dir = os.path.abspath(to_dir)
    zip_name = "setuptools-%s.zip" % version
    url = download_base + zip_name
    saveto = os.path.join(to_dir, zip_name)
    if not os.path.exists(saveto):  # Avoid repeated downloads
        log.warn("Downloading %s", url)
        downloader = downloader_factory()
        downloader(url, saveto)
    return os.path.realpath(saveto)


def _resolve_version(version):
    """
    Resolve LATEST version
    """
    if version is not LATEST:
        return version

    meta_url = urljoin(DEFAULT_URL, '/pypi/setuptools/json')
    resp = urlopen(meta_url)
    with contextlib.closing(resp):
        try:
            charset = resp.info().get_content_charset()
        except Exception:
            # Python 2 compat; assume UTF-8
            charset = 'UTF-8'
        reader = codecs.getreader(charset)
        doc = json.load(reader(resp))

    return str(doc['info']['version'])


def _build_install_args(options):
    """
    Build the arguments to 'python setup.py install' on the setuptools package.

    Returns list of command line arguments.
    """
    return ['--user'] if options.user_install else []


def _parse_args():
    """Parse the command line for options."""
    parser = optparse.OptionParser()
    parser.add_option(
        '--user', dest='user_install', action='store_true', default=False,
        help='install in user site package')
    parser.add_option(
        '--download-base', dest='download_base', metavar="URL",
        default=DEFAULT_URL,
        help='alternative URL from where to download the setuptools package')
    parser.add_option(
        '--insecure', dest='downloader_factory', action='store_const',
        const=lambda: download_file_insecure, default=get_best_downloader,
        help='Use internal, non-validating downloader'
    )
    parser.add_option(
        '--version', help="Specify which version to download",
        default=DEFAULT_VERSION,
    )
    parser.add_option(
        '--to-dir',
        help="Directory to save (and re-use) package",
        default=DEFAULT_SAVE_DIR,
    )
    options, args = parser.parse_args()
    # positional arguments are ignored
    return options


def _download_args(options):
    """Return args for download_setuptools function from cmdline args."""
    return dict(
        version=options.version,
        download_base=options.download_base,
        downloader_factory=options.downloader_factory,
        to_dir=options.to_dir,
    )


def main():
    """Install or upgrade setuptools and EasyInstall."""
    options = _parse_args()
    archive = download_setuptools(**_download_args(options))
    return _install(archive, _build_install_args(options))

if __name__ == '__main__':
    sys.exit(main())

然后安装

$ cd /d/
$ python ez_setup.py

这个时候,可能会有报语法错什么的,和python版本有关,建议下载2.x系列版本的python

4. 安装jekyll

安装

$ gem install jekyll

但是由于下载镜像源的问题,会报错

ERROR:  While executing gem ... (Gem::RemoteFetcher::FetchError)
    Errno::ECONNRESET: An existing connection was forcibly closed by the remote host. - SSL_connect (https://api.rubygems.org/quick/Marshal.4.8/jekyll-3.1.6.gemspec.rz)

需要改变镜像,可以指到淘宝的镜像

$ gem sources --remove https://rubygems.org/
$ gem sources -a https://ruby.taobao.org/

如果不行,换这个

$ gem source -l http://rubygems.org/
$ gem source -a http://production.cf.rubygems.org

然后执行

$ gem install jekyll
Successfully installed colorator-0.1
Successfully installed sass-3.4.22
Successfully installed jekyll-sass-converter-1.4.0
Successfully installed rb-fsevent-0.9.7
Successfully installed ffi-1.9.10-x86-mingw32
Successfully installed rb-inotify-0.9.7
Successfully installed listen-3.0.8
Successfully installed jekyll-watch-1.4.0

这样安装jekyll成功了

$ jekyll -v
jekyll 3.1.6

栅格布局源码分析

栅格变量的定义

在variables.scss文件中定义了不同容器宽度下栅格系统的几个变量:列数($grid-columns总是12列, 列间隙宽度$grid-gutter-width和以及不同尺寸的屏幕宽度。

$screen-xs-min:              480px;
//@screen-phone:               @screen-xs-min;
// Small screen / tablet
//@screen-sm:                  768px;
$screen-sm-min:              768px;
//@screen-tablet:              @screen-sm-min;
// Medium screen / desktop
//@screen-md:                  992px;
$screen-md-min:              992px;
//@screen-desktop:             @screen-md-min;
// Large screen / wide desktop
//@screen-lg:                  1200px;
$screen-lg-min:              1200px;
//@screen-lg-desktop:          @screen-lg-min;
$screen-xs-max: ($screen-sm-min - 1 );
$screen-sm-max: ($screen-sm-min -  1 );
$screen-md-max: ($screen-lg-min - 1 );
//栅格列数
$grid-columns:              12;
//列间距一半为左右的padding值。
$grid-gutter-width:         30px;

公共mixin的定义

  • container-fixed容器定义
@mixin container-fixed($gutter: $grid-gutter-width) {
  margin-right: auto;
  margin-left: auto;
  padding-left:  floor(($gutter / 2));
  padding-right: ceil(($gutter / 2));
  @include clearfix;
  //&:extend(.clearfix all);
}
  • row定义
    @mixin make-row($gutter: $grid-gutter-width) {
      margin-left:  ceil(($gutter / -2));
      margin-right: floor(($gutter / -2));
      box-sizing: border-box;
      @include clearfix;
    }

布局上,通过容器的负padding-left和每一个列的margin-left来实现的

  • 列的定义
@mixin make-grid($type){
  .u-col-#{$type}-push-0{
    left:auto;
  }

  .u-col-#{$type}-pull-0{
    right:auto;
  }

  @for $i from 1 through $grid-columns {
 //根据列数占总列数的比例定义每列的宽度
    .u-col-#{$type}-#{$i}{
      width:percentage($i/$grid-columns);
    }
 //距离左侧的距离
    .u-col-#{$type}-push-#{$i}{
      left: percentage($i/$grid-columns);
    }
//距离右侧的距离
    .u-col-#{$type}-pull-#{$i}{
      right: percentage($i/$grid-columns);
    }
//左侧margin的距离,使用他实现列排序
    .u-col-#{$type}-offset-#{$i}{
      margin-left:percentage($i/$grid-columns);
    }
  }
// _md 类型是为了兼容 u-col-1 这种类名
  @if $type == md {
    .u-col-push-0{
      left:auto;
    }
    .u-col-pull-0{
      right:auto;
    }
    @for $i from 1 through $grid-columns {
      .u-col-#{$i}{
        width:percentage($i/$grid-columns);
      }
      .u-col-push-#{$i}{
        left: percentage($i/$grid-columns);
      }
      .u-col-pull-#{$i}{
        right: percentage($i/$grid-columns);
      }
      .u-col-offset-#{$i}{
        margin-left:percentage($i/$grid-columns);
      }
    }
  }
}

其中最大的亮点就是用递归模拟了循环,通过循环避免了重复写12次代码。

具体代码

固定宽度的栅格布局

.u-container {
  @include container-fixed();

  @media (min-width: $screen-sm-min) {
    width: $container-sm;
  }
  @media (min-width: $screen-md-min) {
    width: $container-md;
  }
  @media (min-width: $screen-lg-min) {
    width: $container-lg;
  }
}

对前端开发中组件、插件、控件的理解

一段时间内,大家都对这些概念含糊不清。而一般地,也很少去用一个很明确的界限来把三者分清。

这三个概念,不仅仅在前端有,在设计上、在其他开发语言中,都有对这几个概念的定义和区分。但这里我们仅限于前端开发领域。下面简单说一下我的想法。

控件

控件是对css样式的高度抽离,我对CSS控件的定义来自于UED的UI视觉设计规范,在一个大型项目中,优秀的UI规范总是能将页面的设计元素提炼出一套标准(比如颜色、字体字号、栅格等)。CSS控件化是从HTML页面结构提炼出的不可分割的最小标签单位。

例如一个icon图标(使用em标签表示)、一段文本(使用p标签)、一个标题(h标签)或者一个按钮(a标签),他们都是构成整个页面的最小结构单位,这就是控件;而控件化还需要控件是具备在整个项目中可复用性强的元素。因此在一个项目中我一般会提取如:btn.css、article.css、icon.css等这样的项目通用控件化模块文件,当然这些控件化模块还可以被分类继续拆分成更灵活的小模块。

每一个控件化模块都用SASS编写,使得属性值均可以变量化,便于UI规范修改我们前端做相应的批量修改。

插件

前端插件指由js封装的可独立提供使用的ui,实现某一类特定的功能和效果。从组成上来说,由js文件或是js和css共同构成,两种方式。

组件

组件由html、js、css、json、图片等资源组成,是页面展现中某一个独立部分,组件可以抽象也可以直接使用,组件可以嵌套组合使用。

基于组件可以封装聚合形成组件库,多个组件构成页面,组件库可以帮助快速开发页面.

框架及官网日常开发流程说明

前端框架源码修改说明

资源库介绍

前端框架修改过程中涉及框架源码库以及资源产出库

框架源码库(release分支维护)

目前框架源码库包括:iuap-desigin、grid、tree、kero、datetimepicker,均在release分支进行维护。

资源产出库(master分支维护)

generate-uui库进行源码资源的整体产出以及CDN资源托管。

在资源产出库通过以下命令可快速下载各个源码库

$ sh fetch.sh

执行命令会将iuap-desigin、grid、tree、kero、datetimepicker资源库下载到当前目录,并且切换到release分支。
建议各个源码库在此库目录下进行维护。

源码修改及提交流程

  • 对各个资源库进行资源修改之后在对应库执行gulp dist。
  • 将原始文件以及dist目录产出资源同时提交。
  • 在generate-uui根目录下执行gulp dist来产出最终资源。(此资源不用提交,看需要是否产出)

官网修改说明:iuap-design.github.io( master分支维护)

官网"开始使用"页签修改

  • 在iuap-design.github.io\docs\getting-started目录下修改对应的md文件

  • 目录下通过以下命令产出html文件

    $ gitbook serve

或者

$ gitbook build

官网"设计语言"页签修改

  • 在iuap-design.github.io\docs\design-language目录下修改对应的md文件

  • 目录下通过以下命令产出html文件

    $ gitbook serve

或者

$ gitbook build

官网"全局css样式"、"组件"页签修改

  • 在iuap-design工程修改snippets目录下资源

  • 执行app.js产出iuap-design下的docs以及examples内容

  • 将iuap-design下docs中的md文件拷贝至以下目录:

    iuap-design.github.io\docs\components

    iuap-design.github.io\docs\global-style

    注:目前需要手工拷贝,并且是从一个目录拷贝到另外一个目录,后续会将iuap-design下的docs生成2个目录并通过命令自动移动至对应目录

  • 在上面的2个目录下通过以下命令产出html文件

    $ gitbook serve

或者

$ gitbook build

官网"JS插件"页签修改

目前还未开发,后续开发过程与“全局css样式”、“组件”流程类似

官网"kero"页签修改

  • 在iuap-design.github.io\docs\kero目录下修改对应的md文件

  • 目录下通过以下命令产出html文件

    $ gitbook serve

或者

$ gitbook build

后续考虑修改流程与“全局css样式”、“组件”同步

官网"模板库"页签修改

  • 前端页面源码:iuap-design.github.io\dist\pages\template
  • 模板源码:

添加模板路径:iuap-design.github.io\dist\pages\website
配置模板页:iuap-design.github.io\src\data\template\index.json
配置需提供的参数
{
"title": "金融理财模板", //模板类型
"id": "0004", //模板id
"decription": "", //模板描述
"content_img": "img/main_6.png", //模板整页截图
"url": "http://design.yyuap.com/ficloud/home/statistics", //模板地址
"download": "javascript:;", //模板下载地址
"using_times": "228", //模板使用次数
"for_area": ["互联网","金融"] //模板适用领域
}

官网"webIDE"页签修改

  • 前端页面源码:iuap-design.github.io\dist\pages\webIDE

后续demos内容的源文件与“全局css样式”、“组件”中demo部分进行整合,webIDE更多提供开发工具方面的内容。

  • 运行对应服务:iuap-design.github.io\server\router.js

官网"定制"页签修改

  • 前端页面源码:iuap-design.github.io\dist\pages\custom
  • 下载服务:uap-design.github.io\server\customized.js

==注意点==:

官网中gitbook插件使用

官网页面除"模板库"、"webIDE"、"定制"页签,其他页签都是通过gitbook插件生成。由于插件在不断更新,如发现插件生成存在问题可通过以下方式更新插件。

  • 删除md文件存放目录下的node_modules下删除gitbook-plugin-iuap-design插件并重新执行

    $ gitbook install

下载最新插件。

  • 直接下载gitbook插件库至md文件存放目录下的node_modules中。插件库地址:

https://github.com/iuap-design/gitbook-plugin-iuap-design

官网中前端模板使用

官网页面"模板库"、"定制"页签使用前端模板进行开发。(卫东补充)

目前只是对当前开发流程进行梳理,发生变动以及未写明部分后续逐步完善。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.