base on jQuery(基于jquery,否则请别继续)
tabs组件
HTML
1 | <div class="tabs"> |
CSS
1 | .tabs{} |
JS
1 | $('.tabs').each(function(index,element) { |
但是这样只是页面上没法复用
面向对象的JS
1 | function Tabs(selector) { |
ES6写法(class)
1 | class Tabs { |
base on jQuery(基于jquery,否则请别继续)
1 | <div class="tabs"> |
1 | .tabs{} |
1 | $('.tabs').each(function(index,element) { |
但是这样只是页面上没法复用
1 | function Tabs(selector) { |
1 | class Tabs { |
标准库的扩充
DOM 的扩充
UI 组件
重点是 UI 组件,因为
正交原则之x/y/z
永远不要用
1 | $('#div').show() |
因为你不知道 div以前display是什么
你应该做的是用状态控制
1 | $('#div').addClass('active') |
你不该首先考虑代码怎么写
你该考虑用户如何调用
同步:等待结果
异步:不等待结果
注意,异步常常伴随回调一起出现,但是异步不是回调,回调也不一定是异步
如下代码就是同步,只要fn内部的循环完毕才能执行下面的代码
1 | function fn(){ |
异步代码
1 | //异步的 asyncFn |
1 | //因为图片还没有加载完毕所以并不知道高度,(请禁用浏览器缓存在执行) |
你应该在图片加载成功后在获取图片的高度
1 | document.getElementsByTagNames('img')[0].onload = function(){ |
为啥点击li每次都打印 6
1 | <li>11111111</li> |
如何解决 把 var i 改为 let i 就可以了
这样调用虽然是同步的,但是页面会失去响应,直到结果回来
1 | let request = $.ajax({ |
1 | //异步方式 |
一般有两种方式拿到异步结果
1 | fs.readFile('./1.txt', (error, content)=>{ |
1 | $.ajax({ |
1 | $.ajax({ |
1 | //成功调第一个函数,失败调用第二个函数 |
Promise只是一种回调的形式
1 | axios({ |
如果是jquery,成功只会走进成功的分支
1 | s1 ==> s2 ==> s3 |
Promise规范是责任制
1 | s1 e1 是第一责任人 |
1 | axios({ |
如果没有 e2 就会报错到浏览器显示给开发者
1 | axios({ |
1 | function buyApple(){ |
1 | function ajax(){ |
Promise 深入阅读:http://www.cnblogs.com/hustskyking/p/promise.html
Promise/A+ 规范:https://segmentfault.com/a/1190000002452115
1 | function buyApple(){ |
10秒之后返回买苹果的结果(成功/失败)
打印2
await的用法就是 后面接一个返回Promise对象的函数
1 | function buyApple(){ |
分析1
1 | var r = await fn().then( |
分析2
1 | var r = await fn().then( |
function(){ return 1 ;} 你声明了它 但是又不能引用到它
1 | // function(){ return 1 ;} 匿名函数 相当于废话 会报错 你只有给了引用才能使用 |
1 | function fn3(){ |
将具名函数赋值给一个变量 它的作用域就变了
1 | var fn4 = function fn5(){return 1;} |
全局作用域的函数 赋值给变量他的作用域不受影响
1 | function fn6(){return 1;} |
1 | var fn8 = ()=>1; //无参数 返回1 |
fn1 调用的时候不会立即执行
而是会生成一个抽象语法树
校验里面的每句话 如果有错误就停止执行
没错再从头开始执行
词法树分析的是语义
只能确认a就是这个函数内部的a
不能确定的是“值”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 var global = 1;
function fn1(param1){
var local1 = 'local1';
var local2 = 'local2';
function fn2(param2){
var local2 = 'fn2 local2';
console.log(local1);
console.log(local2);
}
function fn3(){
var local2 = 'fn3 local2';
fn2(local2);
}
}
1 | var a = 1; |
你在看看
1 | var a =1; |
词法作用域只能确定这个a是不是那个a,不能确定a的值是不是那个值
以后再更新。。。(没人考)
重要[参考链接]
(https://zhuanlan.zhihu.com/p/23804247)
this 是隐藏的第一个参数,且必须是对象
- call的第一个参数就是this
- call的第的哥参数后面的就是arguments(伪数组)
- fn() 等同于 fn.call()
- 非严格模式下,fn()和fn.call() this会转变为window
- fn()是阉割版的fn.call()
1 | function f(){ |
尽可能避免使用fn()的调用形式,因为会让你产生不知道this是谁的恐慌
1 | // 第一步思考 |
1 | // 第二步思考 |
person.sayHi() 等价于 person.sayHi(person)
fn() 等价于 fn.call()
apply的使用场景就是在你不知道要传递的参数有多少个的情况
1 | fn.call(asThis, p1,p2) 是函数的正常调用方式 |
call 和 apply 是直接调用函数,而 bind 则是返回一个新函数(并没有调用原来的函数),这个新函数会 call 原来的函数,call 的参数由你指定。
待更新。。。。。
返回函数的函数
柯里化:将 f(x,y) 变成 f(x=1)(y) 或 f(y=1)x
1 | //柯里化之前 |
柯里化可以将真实计算拖延到最后再做
关于柯里化的高级文章:
柯里化唯一的好处就是惰性求值
在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:
接受一个或多个函数作为输入:forEach sort map filter reduce
1 | //接收一个函数 |
高阶函数的好处就是任意组合
1 | var arr = [1,2,3,4,5,6,7,8]; |
1 | var n = new Number(1) |
1 | function fn(x,y){ |
箭头函数没有this
1 | setTimeout(function(){ |
箭头函数干掉了this
1 | setTimeout(function(){ |
js的漏洞 function(){} 等价于 function(){}.call()
call的第一个参数就是this
call不传递参数就是window
强迫症就是要给箭头函数加this?
答案是不行 会被忽略
1 | var fn =()=>{console.log(this)} |
爸爸是咋叫儿子的!
子组件设置props属性,可以在页面上插值
1 | { |
1 | <div id="app"> |
儿子是怎样叫爸爸的!
1 | <!-- html如下 --> |
如果是爷孙?
vue是不支持的,需要儿子叫爸爸,爸爸叫爷爷,这样层层传递
这里用使用最通俗的方式,你也可以挂载在Vue原型上
1 | Vue.prototype.$eventHub = new Vue() |
重新构造一个Vue对象 专门用来负责管理通信
1 | <!-- html --> |
node官网 下载对应版本(建议下载LTS版本相对稳定)
一路next千万别手欠把add to path 默认选中的取消了
1 | # 查看node 版本 我的是8.9.4 |
1 | npm install 包名 |
找到命令行工具(CLI)
1 | # 全局安装 vue-cli |
以上命令你就可以本地初始化一个 vue的官方模板了
给你一个项目目录(注意给别人项目的时候是不要连带node_modules的)
1 | cd 项目目录 |
使用说明 淘宝镜像如何设置
你可以使用 cnpm (gzip 压缩支持) 命令行工具代替默认的 npm:
1 | $ npm install -g cnpm --registry=https://registry.npm.taobao.org |
安装node-sass
github 搜索 node-sass
cnpm install node-sass -g
-g 这样你就可以任何目录都能执行自动化工具
安装成功
如何使用node-sass
查看文档发现如下
1 | node-sass src/style.scss dest/style.css |
sass语法
嵌套
1 | css写法 |
sass和scss的区别
ruby社区写了sass规则比scss更简洁,但是前端大多数看不懂
aa.sass
1 | body |
aa.css
1
2
3
body p a {
background: red;
}
sass可以自动加“{}” 和“;” 但是新手看不懂啊。。。
> 于是出现了 scss
aa.scss
1
2
3
4
5
6
7
body{
p{
a{
background:red;
}
}
}
> 前端一看: 懂了。。。
但是即使这样我们引用的却不能是scss文件,而是转化后的css文件
改个背景色还要重新编译一次才能看到效果 ? 484傻
google node sass watch
1 | node-sass main.scss main.css -w main.scss |
我们自己写项目可以大量使用ES6,而真实项目可能需要在IE上运行
1 | 安装官方提示的步骤 |
2. 第二步 新建.babelrc
1
2
3
{
"presets": ["env"]
}
3. 第三步点击 向导 点击cli下
> 
> 
4. 继续按照步骤
1
npm install --save-dev babel-cli
5. 由于我们没有pageage.json
1
npm init 一直回车回车回车
6. 因为 npm install --save-dev babel-cli的时候我们还没有 pageage.json
7. 再次运行
1
npm install --save-dev babel-cli
> 你会发现增加了一行
1
2
3
4
5
{
"devDependencies": {
+ "babel-cli": "^6.0.0"
}
}
8. 在pageage.json里添加如下代码
1
2
3
4
5
6
7
8
9
10
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
> 
> 
9. 这个时候提示你可以运行 npm run build
> 此时报错了 ! 因为命令行已经发展到了非常变态的地步,就算你是个老手也会报错
- 因为npm run bulid 实际运行的是 script里的 "babel src -d lib"
- 而"babel src -d lib"会优先寻找./node_modules/.bin/目录
- 此时你就要知道全局安装和局部安装的区别了
> ### 由于我们是局部安装 所以我们要这样运行
1
./node_modules/.bin/babel src -d lib
10. 我们肯定也要时时修改,而不是边保存边编译
1
./node_modules/.bin/babel src -d lib --watch
但是有一个问题 CSS自动化要开一个命令行,JS也要开一个,这样无形中多了好多窗口
改变目录解构
1 | --|app |
node-sass 命令的变化监听目录
1 | node-sass src/css/ -o dist/css/ -w src/css/ |
babel 变化
1 | ./node_modules/.bin/babel src/js -d dist/js --watch |
监听html文件的变化
1 | watch -p "src/index.html" -c "cp src/index.html dist/index.html" |
监听img文件的变化
1 | watch -p "src/img/**/*" -c "cp src/img dist/img" |
在node诞生之后 前端想尽办法代替上面的四个命令行
尝试去试一下这个 秒杀webpack啊简直!
不知有多少人想说 fack webpack
进入官网 点击github
提示你安装
1 | # 建议安装 webpack@3.10.0 因为我用的是这个 这步骤可以先跳过因为你还没有package.json |
继续往下看,发现毫无线索,…. 发 现get started 点击
1 | mkdir webpack_simple |
1 | webpack_simple |
1 | webpack-demo |
1 | const path = require('path'); |
1 | # 注意是 webpack_simple目录下运行如下命令 |
你该新建 .gitignore 文件
1 | node_modules/ |
你需要新建.gitignore文件 内容如上
现在你已经成功的实现打包js
目标引入 babel-loader
知道为啥说三次吗? 因为你不点这个链接估计卡你一宿都不知道为啥错了
webpack 3.x babel-loader 7.x | babel 6.x 我选择了这个 376 于是点击了 7.x branch
Install
webpack 1.x | babel-loader <= 6.x
webpack 2.x | babel-loader >= 7.x (recommended) (^6.2.10 will also work, but with deprecation warnings)
webpack 3.x | babel-loader >= 7.1
1 | yarn add babel-loader babel-core babel-preset-env webpack --dev |
1 | # 设置淘宝镜像 cnpm |
自行脑补然后把module 结构添加到你的webpack.config.js里
1 | const path = require('path'); |
现在你可以写一写ES6语法了
1 | npm install sass-loader node-sass webpack --save-dev |
1 | // webpack.config.js |
1 | const path = require('path'); |
1 | # 在index.js里引入 |
1 | cnpm i style-loader --save-dev |
1 | 最佳实践就是 rm -rf node_modules |
这样你就会陷入绝境,因为别人的配置也不知道是从哪里copy来的他能跑通你就不一定了。出了错你就哭吧!
最佳实践就是照着官方demo写,因为你发现错了,别人还可能跟你遇到同样的问题,这样你还可以参考,一旦你去用了别人的配置,webpack配置这么繁琐,有经验的前端还会翻车呢?更何况你呢?而且你英文不一定很好!
稍微有点相关web性能优化
1 | node.js后台 |
如main.js你缓存了10年但是,你更新后修改了main.js的内容
缓存的策略就是url一致,如果你想更新缓存那就更改url
1 | <script src="main.js?v=1"></script> |
1 | // 文件上加一个随机字符串,这个如果你是webpack会帮你自动生成 |
这样就实现了更新缓存
区别就是:
如果你系统时间是2050年,而缓存时间是2020年,你就没法缓存了
这样不得不提 MD5(摘要算法)
假如你下载一个linux系统400MB 下载过程中如何检测你下载的对不对呢?
1 | 如果网上的 linux系统 400MB MD5值是 XXXXXXXX |
每个文件对应一个 MD5值
文件改动越小MD5差异越大
1 | 引入MD5模块算出 文件对应的MD5值 |
1 | 形如 |
「if-None-Match」就是如果不匹配,就是如果你的MD5跟我一样就不需要下载
1 | if-None-Match:440c53453212aac08c2321sd |
用户可以篡改,假如是多个用户,我只要知道他的Cookie就能冒充他,访问他的隐私信息
Session实质就是服务器的一小块内存——哈希
1 | //node.js服务器 |
1 | //你登录成功后,访问自己的资源时候会传递cookie |
公园系统升级了,因为他发现有人拿着别人的票混进去
于是你每次购票的时候发给你一个票,票上有一个「id号」
1 | //比如 购票者 |
第二天猴哥去公园了,公园就会说 「欢迎你!猴哥」
假如你的session_id = 321341.31239089089054
请问用户2的session_id是什么?我怎么知道用户2的随机数是啥
那我要是删了cookie呢?
当然是重新买票了,或者重新登录喽
为啥是随机数
因为如果是流水号你按顺序排下去就知道别人的session_id了
暴力穷举呢?
有点难啊!
1 | 假如session_id是16位随机数字 |
一个用户大约占据多少内存
如果1M一个用户 1000个用户就是1G内存了
但是实际上每个用户给个 「几K」就够了
html5 提供的api
1 | //注意只能存 string 即使你存一个对象它也会调用对象的 obj.toString() |
session最大问题就是耗内存,因为存在服务器
localStorage存在客户端的盘符里——数据的持久化存储
答案如下
1,2,3,4 同 LocalStorage
session是基于cookie实现的因为session_id必须放到cookie里发给客户端,没有cookie就没有session,
cookie是session的基石
Cookie 是每次请求都带给服务器
Cookie 只有4K
LocalStorage 跟HTTP无关不会被携带到服务器
LocalStorage 有5M
SessionStorage 在用户关闭用户的时候就失效 (会话结束)
实现如下
1 | 后端 |
session大部分时间是基于cookie来存储id的,也可以通过localStorage + 查询参数传递
前端那时无法持久化数据所以跨页面数据有时就使用了 Cookie
问题来了 Cookie每次请求是会被带到服务器的
假设你有1K的cookie这样每个请求就要多携带1K的cookie到服务器,而服务器的数据可能只有 200B (请求就慢了)
就像公园卖票一样,前端就相当于游客,你自己造票?
登陆注册
1 | // node.js后端设置响应头 |
你可以在浏览器里看到你设置的响应头 (Response Headers)
1 | Set-Cookie:sign_in_email=trustfor@sian.cn |
只有被设置了Cookie,你每次访问同一域名下资源都会在请求头里传递 Cookie
1 | Cookie:sign_in_email=trustfor@sian.cn |
后台得到Cookie就知道用户是谁了
将Cookie映射到生活中
- 你去公园,公园给你发了个票,票的有效期是2天
- 假设你中途离开了公园,下午再次进入你就需要提供你的门票
- 即使你是去看公园里的狗,也得有门票才行
问题1
我在Chrome登录了并得到了Cookie,用Safari访问Safari会带上Cookie吗?
No,北京动物园和天津动物园的门票你觉得能一样吗?
问题2
Cookie 存在哪,你的硬盘上(如果是票当然是你的口袋里)
问题3
票能作假吗?
可以,只要你稍微有一点前端知识就可以伪造Cookie
如果是Chrome你可以F12–>Application–>Cookies就能修改了
问题4
Cookie 有有效期吗?
当然有了,你见过7天免登陆吗?
默认有效期20分钟,防止别人拿着你的票查看你隐私信息,
问题5
Cookie 遵守同源策略吗?
也有,不过跟 AJAX 的同源策略稍微有些不同。
当请求 qq.com 下的资源时,浏览器会默认带上 qq.com 对应的 Cookie,不会带上 baidu.com 对应的 Cookie
当请求 v.qq.com 下的资源时,浏览器不仅会带上 v.qq.com 的Cookie,还会带上 qq.com 的 Cookie
另外 Cookie 还可以根据路径做限制,请自行了解,这个功能用得比较少。
这里的场景是非常非常简单的,后台设置cookie肯定不会拿你的邮箱,这样太好冒充了