高阶函数
function Vue(state) {
// 用 Vue.js 编写前端页面
}
function React(state) {
// 用 React.js 编写前端页面
}
function frontend(state, method) {
// 框架无关的方法和思想
}
frontEnd(data, Vue);
frontEnd(data, React);
- 前端框架发展
- 但页面应用基础
- 框架开发思想
前端框架的开发
the World Wide Web begins as a CERN project called ENQUIRE, initiated by Tim Berners-Lee.
- HTML
- HTTP
- URLs
- Httpd and WorldWideWeb
- The first website
- 2016 Turing Award
前端 2005 前
浏览器大战 1 2
JavaScript标准不完善
1995 Java Applet 和 Adobe Flash
JavaScript 工程化
Doug Crockford
- JSON (2001)
- JsLint (2002)
- JsMin (2003)
- JavaScript: The Good Parts (2008)
AJAX 2005
用XHR和后端进行一步交互,可以在不刷新页面更新试图,人们重新思考web的形式,到后来的Web但页应用。
GitHub(2008) 前
- Dojo(2004)
- Prototype(February 2005)
- jQuery(January 2006)
- YUI(February 2006)
- MooTools(September 2006)
GitHub(2008)后
时间节点
- XHR 引出AJAX奠定单页面 应用的基础
- DC(道格) 推动了JS工程化
- github 让前端技术迅速发展
- Chrome V8 引擎让大量JS 执行变得可行,并催生了Node.js 为前端生态化奠定基础
- 随后出现三大SPA 基础与前端规范高速发展
小结
前端历史大体介绍
前端不是太快 而是被压抑
- 规范的完善
- 语言的进化
- 配套设施的完善
第二章节 但页面应用基础
单页应用定义
只加载一个HMTL页面, 并且随着用户的交互汇总,动态加载内容,并且这个过程不刷新页面。
单叶## 标题 ##应用的特点
- 用户体验较高
- 节省服务器计算资源
- 技术门槛
- SEO 首屏幕渲染
单页应用使用场景
- ERP CRM 编辑器 管理后台 对IE10 及其一下没有特殊要求
- 移动端 WEB应用
单页应用基础-history api
用URL 控制试图并且不触发页面刷新
- Hash
- HTML5 History API
- Memory
抽象 history api
- location:path ,query, hash,[state]
- push(path,[state])
- replace(path,[state])
- go(n) -movers history stack by n entries
服务端渲染 -Router
URL | 页面 |
---|---|
/domain/abc | ABC |
/domain/abd | ABD |
/domain/ae | AE |
… | … |
单页应用-Router
URL | 页面 |
---|---|
/domain/a/b/c | ABC |
/domain/a/b/d | ABD |
/domain/a/b/f/g | ABFG |
… | … |
单页应用-Routert
单页应用Router
单页应用-Router
Link与菜单设计
Router 进阶
- 权限控制 — 路由钩子
- 路由代码分块 — 动态路由结合webpack代码分块
- 404,403 页面 — 路由钩子
- 页面动画 — 逻辑+动画库
- URL前缀 — URL 前缀-history api抽象
小结
- 单页应用的特点和使用场景
- 单页应用的路由(框架无关)
- Histroy api
- Router 设计
- 进阶的用法
章节
框架开发思想
- 组件化思想
- 数据驱动
组件化思想
- 组件提取原则
- 组件指责划分
- 高阶组件
组件提取原则
AB + AC 》 A • ( B + C)
组件指责划分
视图和逻辑解耦
- 容器业务组件
- 通用组件
例子
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<script src="https://lib.baomitu.com/jquery/3.1.1/jquery.js"></script>
<div id="temperature1"></div>
<div id="temperature2"></div>
</body>
</html>
//js
function Temperature(selector, C = 0) {
var container = $(selector);
container.append(
`<div>
<label> 摄氏度 </label>
<input type="number" class="left"/> =
<label> 华氏度 </label>
<input disabled type="number" class="right"/>
</div>`
);
container = container.find('div').last();
var leftEle = container.find('.left');
var rightEle = container.find('.right');
leftEle.on('input', e => updateValue(e.target.value));
function updateValue(c) {
C = c;
leftEle.val(c);
rightEle.val(c * 1.8 + 32);
}
updateValue(C);
return leftEle;
}
var a = Temperature('#temperature1', 100);
var b = Temperature('#temperature1', 100);
//js 版本2
function Temperature(selector, left, right, parse, C = 0) {
var container = $(selector);
container.append(
`<div>
<label> ${left} </label>
<input type="number" class="left"/> =
<label> ${right} </label>
<input disabled type="number" class="right"/>
</div>`
);
container = container.find('div').last();
var leftEle = container.find('.left');
var rightEle = container.find('.right');
leftEle.on('input', e => updateValue(e.target.value));
function updateValue(c) {
C = c;
leftEle.val(c);
rightEle.val(parse(c));
}
updateValue(C);
return leftEle;
}
var parseA = v => v * 1.8 + 32;
var parseB = v => v * 2.2046226;
var parseC = v => v * 6.7547;
var parseD = v => v / 6.7547;
Temperature('#temperature1', '摄氏度', '华氏度', parseA, 100);
Temperature('#temperature1', ' 千克', ' 磅', parseB, 100);
Temperature('#temperature1', '人民币', ' 美元', parseC, 100);
Temperature('#temperature1', ' 美元', '人民币', parseD, 100);
组件指责划分小结
- 通用组件应当是失学模型
- 纯展示组件 无状态组件
- 容器组件放置业务逻辑
视图和逻辑结构MV* 模型
高阶组件
满足以下条件之一可以为认为是高阶组件
- 使用了其他的组件
- 接受组件作为参数
- 基本都是高阶组件
高阶组件
- 简化配置
- 抽象行为
- 抽象数据
- 抽象样式
- 抽象…
<Cabinet>
<Cabinet.Left>
<SearchBar />
<Button>批量查询</Button>
<BusinessSelect />
</Cabinet.Left>
<Cabinet.Right>
<Button>修改配置</Button>
<Button>新建实例</Button>
<Button>标签维护</Button>
<DropdownButton title="实例操作"/>
</Cabinet.Right>
</Cabinet>
<Fieldset>
<IDCSearch/>
<Fieldset.Row>
{filters.map((filter, i) =>
<div key={i}>
<label>{filter.label}</label>
<Select.Single/>
</div>
)}
</Fieldset.Row>
</Fieldset>
小结
- 介绍组件化思想的几个要点
- 抽象思想上类似函数式编程的结合,高阶等概念
数据驱动开发
- 把问题抽象成数据(完备的)
- 数据正交化
- 视图是数据的映射
- 设计可以用数据驱动的组件
//版本三
function Temperature(selector, left, right, parse, C = 0) {
var container = $(selector);
container.append(
`<div>
<label> ${left} </label>
<input type="number" class="left"/> =
<label> ${right} </label>
<input disabled type="number" class="right"/>
</div>`
);
container = container.find('div').last();
var leftEle = container.find('.left');
var rightEle = container.find('.right');
leftEle.on('input', e => updateValue(e.target.value));
function updateValue(c) {
C = c;
leftEle.val(c);
rightEle.val(parse(c));
}
updateValue(C);
return leftEle;
}
var parseA = v => v * 1.8 + 32;
var parseB = v => v * 2.2046226;
var parseC = v => v * 6.7547;
var parseD = v => v / 6.7547;
Temperature('#temperature1', '摄氏度', '华氏度', parseA, 100);
Temperature('#temperature1', ' 千克', ' 磅', parseB, 100);
Temperature('#temperature1', '人民币', ' 美元', parseC, 100);
Temperature('#temperature1', ' 美元', '人民币', parseD, 100);
版本4
function Temperature(selector, left, right, parse, C = 0) {
var container = $(selector);
container.append(
`<div>
<label> ${left} </label>
<input type="number" class="left"/> =
<label> ${right} </label>
<input disabled type="number" class="right"/>
</div>`
);
container = container.find('div').last();
var leftEle = container.find('.left');
var rightEle = container.find('.right');
leftEle.on('input', e => updateValue(e.target.value));
function updateValue(c) {
C = c;
leftEle.val(c);
rightEle.val(parse(c));
}
updateValue(C);
return leftEle;
}
var parseA = v => v * 1.8 + 32;
var parseB = v => v * 2.2046226;
var parseC = v => v * 6.7547;
var parseD = v => v / 6.7547;
var a = Temperature('#temperature1', '摄氏度', '华氏度', parseA, 100);
var b = Temperature('#temperature1', ' 千克', ' 磅', parseB, 100);
var c = Temperature('#temperature1', '人民币', ' 美元', parseC, 100);
var d = Temperature('#temperature1', ' 美元', '人民币', parseD, 100);
a.change(updateMessage);
b.change(updateMessage);
c.change(updateMessage);
d.change(updateMessage);
function updateMessage() {
var message = `
<div>
<p>${a.val()}摄氏度 = ${parseA(a.val())}华氏度</p>
<p>${b.val()}千克 = ${parseB(b.val())}华氏度</p>
<p>${c.val()}人民币 = ${parseC(c.val())}美元</p>
<p>${d.val()}美元 = ${parseD(d.val())}人民币</p>
</div>`;
$('#temperature2').empty().append(message);
}
updateMessage();
出现重复代码,需要重构了。在编码道路上如履薄冰,为什么渐行渐远
数据是本质,视图是元凶
var data = [
[100, '摄氏度', '华氏度', 212],
[100, ' 千克', ' 磅', 220.46226],
[100, '人民币', ' 美元', 675.47],
[100, ' 美元', '人民币', 14.80]
]
数据驱动之
把问题抽象成数据
- 数据描述对问题时间轴完备性
数据正交化
- 数据互不关联
- 多种正交形式
数据时间轴完备性和正交化
var data = [
[100, '摄氏度', '华氏度', 212],
[100, ' 千克', ' 磅', 220.46226],
[100, '人民币', ' 美元', 675.47],
[100, ' 美元', '人民币', 14.804506491776097]
]
/* 形式二 */
var data = [
[100, '摄氏度', '华氏度', v => v * 1.8 + 32],
[100, ' 千克', ' 磅', v => v * 2.2046226],
[100, '人民币', ' 美元', v => v / 6.7547],
[100, ' 美元', '人民币', v => v * 6.7547]
]
/* 形式三 */
var exchange = 6.7547;
var data = [
[100, '摄氏度', '华氏度', v => v * 1.8 + 32],
[100, ' 千克', ' 磅', v => v * 2.2046226],
[100, '人民币', ' 美元', v => v / exchange],
[100, ' 美元', '人民币', v => v * exchange]
];
- 产品 :每隔3秒动态获取以下汇率把
- 开发:数据驱动
var exchange = 6.7547;
var data = [
[100, '摄氏度', '华氏度', v => v * 1.8 + 32],
[100, ' 千克', ' 磅', v => v * 2.2046226],
[100, '人民币', ' 美元', v => v / exchange],
[100, ' 美元', '人民币', v => v * exchange]
];
//版本5
function ConvertInput(selector, C = 0, left, right, parse) {
var container = $(selector);
container.append(
`<div>
<label> ${left} </label>
<input type="number" class="left"/> =
<label> ${right} </label>
<input disabled type="number" class="right"/>
</div>`
);
container = container.find('div').last();
var leftEle = container.find('.left');
var rightEle = container.find('.right');
leftEle.on('input', e => updateValue(e.target.value));
function updateValue(c) {
C = c;
leftEle.val(c);
rightEle.val(parse(c));
}
updateValue(C);
return leftEle;
}
var exchange = 6.75;
var a = [100, '摄氏度', '华氏度', v => v * 1.8 + 32];
var b = [100, ' 千克', ' 磅', v => v * 2.2046226];
var c = [100, '人民币', ' 美元', v => v / exchange];
var d = [100, ' 美元', '人民币', v => v * exchange];
var list = [a, b, c, d];
setInterval(()=>{
exchange = 6.75 + Math.random() * - .5;
render()
}, 3000);
function renderMessage() {
var message = list.map(item => `<p>${item[0]}${item[1]} = ${item[3](item[0])}${item[2]}</p>`).join('\n');
message = `<div>${message}</div>`;
$('#messages').empty().append(message);
}
function render() {
$('#inputs').empty();
list.forEach(item => ConvertInput('#inputs', ...item).on('input', e => {
item[0] = e.target.value;
console.log(item[0])
renderMessage();
}));
renderMessage();
}
render();
使用框架
框架能处理好数据到视图的绑定 目前主流框架都是单项数据流
设计可以用数据驱动的组件
数据驱动从数据出发
组件对数据无侵入
组件对数据无要求Array
const options = [
{label: 'label1', id: '1'},
{label: 'label2', id: '2'},
{label: 'label3', id: '3'}
]
const computedOptions = option.map(i=>({value: i.id, name: i.label})
<Select options={computedOptions} />
//
const options = [
{label: 'label1', id: '1'},
{label: 'label2', id: '2'},
{label: 'label3', id: '3'}
]
<HocSelect options={computedOptions}
map={ i=> ({name: i.label, value: i.id}) }
/>
// --------------------------------
const options2 = [
['label1','1'],
['label2','2'],
['label3','3']
]
<HocSelect options={computedOptions}
map={ i=> ({name: i[0], value: i[1]}) }
/>
组件对数据无要求Tree
const options = {label: 'label1', id: '1', children: [
{label: 'label2', id: '2'},
{label: 'label3', id: '3'}
]}
<HocTreeSelect options={computedOptions}
childrenKey="children"
map={ i=> ({name: i.label, value: i.id}) }
/>
// lifting
// 不管 value 是单个还是数组都能工作
[].concat(value)
小结
- 把问题抽象成数据(完备的)
- 数据正交化
- 视图是数据的映射
- 设计可以用数据驱动的组件
WEB应用挑战和机遇
技术门槛变高
兼容性问题
移动端支持和设计
安全问题
性能和体验期望高
参考文档