卡牌大叔的博客

  • Home

  • Archives

Vue笔记

Posted on 2018-01-24 | Edited on 2019-06-02

响应式原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
var data = { name:"poetries "}
observe(data)
function update(value) {
document.querySelector('div').innerText = value
}
new Watcher(data,'name',update)

data.name = 'yyy'


function observe(obj) {
if(!obj||typeof obj!=='object'){
return
}
Object.keys(obj).forEach(key=>{
defineReactive(obj,key,obj[key])
})
}

function defineReactive(obj,key,val) {
observe(val)
let dep = new Dep()
Object.defineProperty(obj,key,{
enumerable:true,

configurable:true,

get:function reactiveGetter() {
console.log('get value')
if(Dep.target){
dep.addSub(Dep.target)
}
return val
},

set:function reactiveSetter(newValue) {
console.log('change value')
val = newValue
dep.notify()
}
})
}


class Dep{
constructor(){
this.subs=[]
}
addSub(sub){
this.subs.push(sub)
}
notify(){
this.subs.forEach(sub=>{
sub.update()
})
}
}
Dep.target=null

class Watcher{
constructor(obj,key,cb){

Dep.target =this
this.cb=cb
this.obj=obj
this.key=key
this.value = obj[key]
Dep.target = null

}
update(){
this.value = this.obj[this.key]

this.cb(this.value)
}

}

//Vue有几个问题,Object.defineProperty不能拦截数组操作
//新增对象属性不触发组件渲染的问题
//Vue通过函数重写的方式解决了这个问题。

export function set(target:Array<any>|Object,key:any,val:any):any{
if(Array.isArray(target)&&isValidArrayIndex(key)){
//调用splice函数派发更新
target.length = Math.max(target.length,key)
target.splice(key,1,val)
return val

}
if(key in target &&!(key in Object.prototype)){
target[key] = val
return val
}

const ob =(target:any).__ob__

if(!ob){
target[key] = val
return val
}

defineReactive(ob.value,key,value)

ob.dep.notify()

return val

}
//解决Array的一些方法无法触发响应式的问题。
const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)

const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]

methodsToPatch.forEach(function (method) {

const original = arrayProto[method]

def(arrayMethods,method,function mutator(...args){

const result = original.apply(this,args)
const ob =this.__ob__
let inserted

switch (method) {
case 'push':
case 'unshift':
inserted = args
break;
case 'splice':
inserted = args.slice(2)
break;
}
if(inserted) ob.observeArray(inserted)
ob.dep.notify()
return result
})
})

router-link用法

  1. router-link默认渲染为

    href
    1
    2
    3
    4
    5
    6
    ```html
    <router-link tag="a" :to="'/city/' + guessCityid" class="guess_city">
    <span>{{guessCity}}</span>
    </router-link>
    渲染为
    <a data-v-3ea254f4="" href="#/city/2" class="guess_city"><span data-v-3ea254f4="">杭州</span></a>

  2. router-link tag属性可设置为li,span等等,渲染为li,span

  3. :to内有path, query参数 path负责切换路由,query为路由参数

1
2
3
4
<router-link :to="{path: '/food', query: {geohash, title: foodItem.title, restaurant_category_id: getCategoryId(foodItem.link)}}" v-for="foodItem in item" :key="foodItem.id" class="link_to_food">
</router-link>
渲染为
<a data-v-070ab150="" href="#/food?geohash=30.26276,120.21403&amp;title=%E5%95%86%E8%B6%85%E4%BE%BF%E5%88%A9&amp;restaurant_category_id=252" class="link_to_food"></a>

Vue项目中props,data()中数据的区别

  • props用于存放模板,其他函数运行时需要的变量;接住父模板传递过来的参数;也可以是模板运行时的函数名称。
  • data(){ }存放响应式的变量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    export default {
    data(){
    return {
    offset: 0, // 批次加载店铺列表,每次加载20个 limit = 20
    shopListArr:[], // 店铺列表数据
    preventRepeatReuqest: false, //到达底部加载数据,防止重复加载
    showBackStatus: false, //显示返回顶部按钮
    showLoading: true, //显示加载动画
    touchend: false, //没有更多数据
    imgBaseUrl,
    }
    },
    mounted(){
    this.initData();
    },
    components: {
    loading,
    ratingStar,
    },
    props: ['restaurantCategoryId', 'restaurantCategoryIds', 'sortByType', 'deliveryMode', 'supportIds', 'confirmSelect', 'geohash'],
    mixins: [loadMore, getImgPath],
    computed: {
    ...mapState([
    'latitude','longitude'
    ]),
    },
    updated(){
    // console.log(this.supportIds, this.sortByType)
    },
    methods: {
    async initData(){
    //获取数据
    let res = await shopList(this.latitude, this.longitude, this.offset, this.restaurantCategoryId);
    ....
    },
    //到达底部加载更多数据
    async loaderMore(){
    ...
    },
    //返回顶部
    backTop(){
    ...
    },
    //监听父级传来的数据发生变化时,触发此函数重新根据属性值获取数据
    async listenPropChange(){
    ...
    },

    },
    watch: {
    //监听父级传来的restaurantCategoryIds,当值发生变化的时候重新获取餐馆数据,作用于排序和筛选
    restaurantCategoryIds: function (value){
    this.listenPropChange();
    },
    //监听父级传来的排序方式
    sortByType: function (value){
    this.listenPropChange();
    },
    //监听父级的确认按钮是否被点击,并且返回一个自定义事件通知父级,已经接收到数据,此时父级才可以清除已选状态
    confirmSelect: function (value){
    this.listenPropChange();
    }
    }
    }

Vue项目中模板传递数据,条件判断写法

Head.vue 抽象基础模板

  • 对外暴露的参数写法均为驼峰式 goBack headTitle signinUp
  • slot name = ‘logo/search/edit’ 占位卡槽
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<header id='head_top'>
<slot name='logo'></slot>
<slot name='search'></slot>
<section class="head_goback" v-if="goBack" @click="$router.go(-1)">
</section>
<router-link :to="userInfo? '/profile':'/login'" v-if='signinUp' class="head_login">
<svg class="user_avatar" v-if="userInfo">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#user"></use>
</svg>
<span class="login_span" v-else>登录|注册</span>
</router-link>
<section class="title_head ellipsis" v-if="headTitle">
<span class="title_text">{{headTitle}}</span>
</section>
<slot name="edit"></slot>
</header>
</template>

Home.vue

* 将head.vue作为基础模板引入

  • 传递参数用 连接字符命名 signin-up对应参数signinUp
  • slot
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    ```html
    <template>
    <div>
    <head-top signin-up='home'>
    <span slot='logo' class="head_logo" @click="reload"><!--ele.me-->首页</span>
    <!--传入公共模板head.vue的占位卡槽-->
    </head-top>
    ...
    </div>
    </template>
    <script>
    import headTop from 'head.vue'
    export default {
    data(){ }
    mounted(){ }
    components:{
    headTop //模板中可以使用head-top载入
    },
    methods:{
    //点击图标刷新页面
    reload(){
    window.location.reload();
    }
    }
    }
    </script>
  • 几种数据展示方式

  1. head-top 导入的组件
  2. :head-title -> v-bind:head-title go-back 子模板接口
  3. cityname placelist nextpage 当前模板变量小写名称,函数名称
  4. slot=”changecity”卡槽名称
  5. 不在””内,对外暴露的模板参数
  6. class=”title_head ellipsis” 模板类名
  7. v-for=”(item, index) in placelist” each数据操作
  8. @click 模板绑定方法
  9. :key=”index” ->v-bind:key=”index” 性能优化方法
  10. 不在””内,当前对象的字面量值
  11. :class=”{choose_type:sortBy == ‘food’}” class判断逻辑

一小段代码就能弄出11条指令的不同用法,Vue其实也不方便。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

parent1.vue
<head-top :head-title="cityname" go-back='true'>
<router-link to="/home" slot="changecity" class="change_city">切换城市</router-link>
</head-top>

<li v-for="(item, index) in placelist" @click='nextpage(index, item.geohash)' :key="index">
<div class="sort_item" :class="{choose_type:sortBy == 'food'}" >
<h4 class="pois_name ellipsis">{{item.name}}</h4>
<p class="pois_address ellipsis">{{item.address}}</p>
</li>

basic.vue
<section class="title_head ellipsis" v-if="headTitle">
<span class="title_text">{{headTitle}}</span>
</section>

webpack中利用require.ensure()实现按需加载

  1. 空数组作为参数
1
2
3
require.ensure([], function(require){
require('./a.js');
});

以上代码保证了拆分点被创建,而且 a.js 被 webpack 分开打包。

  1. 依赖作为参数
    1
    2
    3
    require.ensure(['./a.js'], function(require) {
    require('./b.js');
    });

  上面代码, a.js 和 b.js 都被打包到一起,而且从主文件束中拆分出来。但只有 b.js 的内容被执行。a.js 的内容仅仅是可被使用,但并没有被输出。

  想去执行 a.js,我们需要异步地引用它,如 require(‘./a.js’),让它的 JavaScritp 被执行。

  1. 单独打包成自己写的名字配置
      需要配置chunkFilename,和publicPath。publicPath是按需加载单独打包出来的chunk是以publicPath会基准来存放的,chunkFilename:[name].js这样才会最终生成正确的路径和名字
    1
    2
    3
    4
    5
    6
    7
    8
    module.exports={
    entry:'./js/entry.js',
    output:{
    path:path.resolve(__dirname,"./dist"),
    filename:'js/a.bundle.js',
    publicPath:"./",
    chunkFilename:'js/[name].js'
    }

所以router/index.js 修改为懒加载组件:

1
2
3
4
const Province = r => require.ensure([], () => r(require('@/components/Province.vue')), 'chunkname1')
const Segment = r => require.ensure([], () => r(require('@/components/Segment.vue')), 'chunkname2')
const Loading = r => require.ensure([], () => r(require('@/components/Loading.vue')), 'chunkname3')
const User = r => require.ensure([], () => r(require('@/components/User.vue')), 'chunkname3')

  根据 chunkame的不同, 上面的四个组件, 将会被分成3个块打包,最终打包之后与组件相关的js文件会分为3个 (除了app.js,manifest.js, vendor.js)


VueRouter代码详解

main.js
import routes from ‘./router/router’
console.log(routes)

/router/router.js
import App from ‘../App’
console.log(App);


Vuex 用Object.assign 修改数据

src
1
2
3
4
//修改用户名
[RETSET_NAME](state,username) {
state.userInfo = Object.assign({}, state.userInfo,{ username })
},

VuePress文档系统搭建

链接


深度观测如何实现?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function _traverse (val: any, seen: SimpleSet) {
let i, keys
const isA = Array.isArray(val)
if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode) {
return
}
if (val.__ob__) {
const depId = val.__ob__.dep.id
if (seen.has(depId)) { return }
seen.add(depId)
} if (isA) {
i = val.length
while (i—)
_traverse(val[i], seen)
} else {
keys = Object.keys(val)
i = keys.length
while (i—)
_traverse(val[keys[i]], seen)
}
}
get(){
pushTarget(this)
...
finally{
if (this.deep) {
traverse(value) ////这深度观测有毛用,value也拿不到啊
}
popTarget()
this.cleanupDeps()
}
}

最新版的nextTick不是走宏任务,微任务的逻辑,是根据终端的特性判断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
if (typeof Promise !== 'undefined' && isNative(Promise)) {
const p = Promise.resolve()
timerFunc = () => {
p.then(flushCallbacks)
if (isIOS) setTimeout(noop)
}
}else if (!isIE && typeof MutationObserver !== 'undefined' && (isNative(MutationObserver) || MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
let counter = 1
const observer = new MutationObserver(flushCallbacks)
const textNode = document.createTextNode(String(counter))
observer.observe(textNode, {
characterData: true
})
timerFunc = () => {
counter = (counter + 1) % 2
textNode.data = String(counter)
}
}else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
timerFunc = () => {
setImmediate(flushCallbacks)
}
}else{
timerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}
```

## Mutation Observer API
[使用MutationObserver跟踪DOM的改变 ] (https://segmentfault.com/a/1190000014738092)

##nextTick如何理解 ??
其实最好理解的方式就是把 nextTick 看做 setTimeout(fn, 0)
Vue 是一个数据驱动的框架,如果能在UI重渲染之前更新所有数据状态,这对性能的提升是一个很大的帮助,所有要优先选用 microtask(微任务) 去更新数据状态而不是 (macro)task(宏任务),这就是为什么不使用 setTimeout 的原因,因为 setTimeout 会将回调放到 (macro)task 队列中而不是 microtask 队列,所以理论上最优的选择是使用 Promise,当浏览器不支持 Promise 时再降级为 setTimeout
UIWebViews 中存在很奇怪的问题,即 microtask 没有被刷新,对于这个问题的解决方案就是让浏览做一些其他的事情比如注册一个 (macro)task 即使这个 (macro)task 什么都不做,这样就能够间接触发 microtask 的刷新。

if (typeof Promise !== ‘undefined’ && isNative(Promise)) {
const p = Promise.resolve()
timerFunc = () => {
p.then(flushCallbacks)
// In problematic UIWebViews, Promise.then doesn’t completely break, but
// it can get stuck in a weird state where callbacks are pushed into the
// microtask queue but the queue isn’t being flushed, until the browser
// needs to do some other work, e.g. handle a timer. Therefore we can
// “force” the microtask queue to be flushed by adding an empty timer.
if (isIOS) setTimeout(noop)
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

## 什么是异步更新队列?
queueWatcher 函数的作用就是我们前面讲到过的,它将观察者放到一个队列中等待所有突变完成之后统一执行更新。

## !this.depIds.has(id) { dep.addSub(this) } 在依赖收集逻辑里,这条语句为何可以控制重复收集?
* 1、newDepIds 属性用来在一次求值中避免收集重复的观察者
* 2、每次求值并收集观察者完成之后会清空 newDepIds 和 newDeps 这两个属性的值,并且在被清空之前把值分别赋给了 depIds 属性和 deps 属性
* 3、depIds 属性用来避免重复求值时收集重复的观察者
通过以上三点内容我们可以总结出一个结论,即 newDepIds 和 newDeps 这两个属性的值所存储的总是当次求值所收集到的 Dep 实例对象,而 depIds 和 deps 这两个属性的值所存储的总是上一次求值过程中所收集到的 Dep 实例对象。
```js
addDep (dep: Dep) {
const id = dep.id
if (!this.newDepIds.has(id)) {
this.newDepIds.add(id)
this.newDeps.push(dep)
if (!this.depIds.has(id)) {
dep.addSub(this)
}
}
}

答:watch 在get函数中 finally 语句块内调用了观察者对象的 cleanupDeps 方法,这个方法的作用在于 每次求值完毕后都会使用 depIds 属性和 deps 属性保存 newDepIds 属性和 newDeps 属性的值,然后再清空 newDepIds 属性和 newDeps 属性的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
get () {
pushTarget(this)
let value
const vm = this.vm
try {
value = this.getter.call(vm, vm)
} catch (e) {
....
} finally {
if (this.deep) { traverse(value)
}
popTarget()
this.cleanupDeps()
}
return value
}
cleanupDeps () {
let i = this.deps.length
while (i--) {
const dep = this.deps[i]
if (!this.newDepIds.has(dep.id)) {
dep.removeSub(this)
}
}
let tmp = this.depIds
this.depIds = this.newDepIds
this.newDepIds = tmp
this.newDepIds.clear()
tmp = this.deps
this.deps = this.newDeps
this.newDeps = tmp
this.newDeps.length = 0
}

vm._render 函数的作用是调用 vm.$options.render 函数并返回生成的虚拟节点(vnode)
vm._update 函数的作用是把 vm._render 函数生成的虚拟节点渲染成真正的DOM

问题?1虚拟节点vnode是什么?

2如何渲染虚拟节点
vm.$el是什么?


1
2
3
4
5
const vm = new Vue({
el: '#foo',
template: '<div id="bar"></div>’
})
vm.$el 是 id 为 bar 的 div 的引用 即为'<div id="bar"></div>

理解 Vue中的Render渲染函数 ,Vue.createElement方法

link

Vue.set不能在根节点上添加属性。

1
2
3
4
5
6
7
8
9
10
11
12
const data = {
obj: {
a: 1
__ob__ // ob2
},
__ob__ // ob1
}
new Vue({
data
})
Vue.set(data, 'someProperty', 'someVal')
//不允许在根对象上添加属性

Vue.set是干什么用的?

Vue 是没有能力拦截到为一个对象(或数组)添加属性(或元素)的,
而 Vue.set 和 Vue.delete 就是为了解决这个问题而诞生的。同时为了方便使用

1
2
3
4
5
6
const ins = new Vue({
data: {
arr: [1, 2] }
})
ins.$data.arr[0] = 3 // 不能触发响应
ins.$set(ins.$data.arr, 0, 3) // 能够触发响应

##如何理解Vue里defineReactive这个函数?
defineReactive 这个函数包含 Object.defineProperty这个关键函数。在Vue源码里主要负责取data中的key,value。

在Object.defineProperty 外部new Dep实例。 为每个data的key 闭包绑定一个Dep,负责在get,set外部传入Dep

get 一个是返回正确的属性值,另一个是收集依赖。 Dep.target.depend()

set 函数也要完成两个重要的事情,第一正确地为属性设置新值,第二是能够触发相应的依赖。notify

Vue.set如何触发依赖收集?

$set 或 Vue.set 给数据对象添加新属性时触发,我们知道由于 js 语言的限制,在没有 Proxy 之前 Vue 没办法拦截到给对象添加属性的操作。

所以 Vue 才提供了 $set 和 Vue.set 等方法让我们有能力给对象添加新属性的同时触发依赖,那么触发依赖是怎么做到的呢?

1
2
3
4
Vue.set = function (obj, key, val) {
defineReactive(obj, key, val)
obj.__ob__.dep.notify()
}

notify 调用watch.run,确认data依赖,变更oldVal = newVal, 执行callback

vue2.0中的组件的继承与扩展

link

一、slot
1.默认插槽和匿名插槽

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
<div id="itany">
<my-hello>180812</my-hello>
</div>
<template id="hello">
<div>
<h3>welcome to xiamen</h3>
<slot>如果没有原内容,则显示该内容</slot> //默认插槽
</div>
</template>
<script>
var vm =new Vue({
el:'#itany',
components:{
'my-hello':{
template:'#hello'
}
}
})
</script>
</body>

2.具名插槽

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<div id="itany">
<my-hello>
<ul slot="s1">
<li>aaa</li>
<li>bbb</li>
<li>ccc</li>
</ul>
<ol slot="s2">
<li>111</li>
<li>222</li>
<li>333</li>
</ol>
</my-hello>
</div>
<template id="hello">
<div>
<slot name="s2"></slot>
<h3>welcome to xiamen</h3>
<slot name="s1"></slot>
</div>
</template>
<script>
var vm = new Vue({
el:'#itany',
components:{
'my-hello':{
template:'#hello'
}
}
})
</script>

二、mixins

1
2
3
一般有两种用途:
1、在你已经写好了构造器后,需要增加方法或者临时的活动时使用的方法,这时用混入会减少源代码的污染。
2、很多地方都会用到的公用方法,用混入的方法可以减少代码量,实现代码重用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<h1>Mixins</h1>
<div id="app">
<p>num:{{ num }}</p>
<p>
<button @click='add'>增加数量</button>
</p>
</div>
<script>
var addLog = {
updated:function(){
console.log("数据发生变化,变化成"+this.num+".");
}
}
Vue.Mixin({
updated:function(){
console.log("我是全局的混入");
//执行顺序.全局混入的执行顺序要前于混入和组件里的方法。
}
})
var app = new Vue({
el:'#app',
data:{
num:1
},
methods:{
add:function(){
this.num++;
}
},
updated(){
console.log("我是原生update");
},
mixins:[addLog]
})
</script>

三丶extends
1 extends用法
extends选项允许声明扩展另一个组件,而无需使用 Vue.extend。它和混入mixins非常的类似。只不过接收的参数是简单的选项对象或构造函数,所以extends只能单次扩展一个组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<h1>Extends</h1>
<hr>
<div id="app">
num:{{ num }}
<p>
<button @click="add">add</button>
</p>
</div>
<script type="text/javascript">
var bbb = {
updated() {
console.log("我是被扩展出来的");
},
methods: {
add: function () { //跟原生的方法冲突,取原生的方法,这点跟混入一样
console.log('我是被扩展出来的add方法!');
this.num++;
}
}
};
var app = new Vue({
el: '#app',
data: {
num: 1
},
methods: {
add: function () {
console.log('我是原生add方法');
this.num++;
}
},
updated() {
console.log("我是扩展出来的");
},
extends: bbb //<font color="red">接收对象和函数</font>
})
</script>

四 extend
Vue.extend只是创建一个构造器,它是为了创建可复用的组件。其主要用来服务于Vue.component,用来生成组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div id="itany">
<hello></hello>
<my-world></my-world>
</div>
<script>
//1.使用Vue.extend()创建一个组件构造器
var MyComponent = Vue.extend({
template: '<h3>Hello World</h3>'
});
//2.使用Vue.component(标签名,组件构造器),根据组件构造器来创建组件
Vue.component('hello', MyComponent);
Vue.component('my-world', {
template: '<h1>你好,世界</h1>'
});
var vm = new Vue({ //这里的vm也是一个组件,称为根组件Root
el: '#itany',
data: {}
});
</script>

propsdata是干什么的?

propsdata 创建实例时传递 props。主要作用是方便测试。

1
2
3
4
5
6
7
8
9
var Comp = Vue.extend({
props: ['msg'],
template: '<div>{{ msg }}</div>'
})
var vm = new Comp({
propsData: {
msg: 'hello'
}
})

Vue源码里选项合并有两种方式

1 主动调用 外部调用场景new Vue(options) 的方式实例化一个 Vue 对象;另一种是我们上一节分析的组件过程中内部通过 new Vue(options) 实例化子组件。
都会执行实例的 _init(options) 方法,它首先会执行一个 merge options 的逻辑,相关的代码在 src/core/instance/init.js 中:
2 组件场景
Vue.extend父子传递, extends父子传递,mixins, new Vue等等,propsData父子传递,如何对父子选项进行合并处理的,也知道了它的作用
源代码看起来总之很麻烦


怎么看Vue源代码?

发现Vue源码里typescript的语法很适合看代码。有参数函数返回值,非常直观。
配合文档https://cn.vuejs.org/ http://hcysun.me/vue-design/一步一步看还是比较舒服的。


Vue生命周期图详解 https://segmentfault.com/a/1190000008570622
Vue2.0 $set() https://blog.csdn.net/panyang01/article/details/76665448
mounted () {
this.$set(this.student,”age”, 24) $set()方法,既可以新增属性,又可以触发视图更新。
}


如何实现一个简单的Vue?

  • 每一个v-html, v-text等带有的表达式 内部都有一个Watch对象,在compile模板的过程中,递归fragment,绑定对应事件过程进行的。
  • 编译过程为逐行编译,并非统一编译。 比如碰到一个 编译过程中直接填上data ,new一个watcher绑定到当前指令上,watcher内部再取一次
    的值,触发一次this.get( ) Dep.target = this; 拿到name的值,这个过程中触发defineProperty get( )建立 dep和watch的关系。
  • watch里的this.depIds ={ } 把dep 压入进去, dep里的 this.subs=[ ] 把watch放进去
  • 一个watch里面只能有一个dep, 一个dep里面可以有多个watch (dep其实就是可动态更改的对象数目)
    (watch是模板指令数目)对象在模板上可以对应多个指令进行模板修改。
  • 当method通过addEventListener触发是,Observer监测到对象有变化,在set( )方法里调用Dep的notify方法,对保存在Dep里的Watch进行迭代,调用Watch里的update方法,update方法拿到新set的对象的值,对比老对象的值,调用各个指令节点绑定的方法,进行指令替换。


new MVVM 构造函数里。

  1. 解析指令 解析模板里的指令。
    初始化视i图-> 逐行解析指令,把属性值填入到指令里。
    订阅数据变化-> 把属性值填入指令里下面接着就绑定给指令节点绑定更新函数,并new一个Watch对象。绑定到当前指令上,watcher构建的时再取
    指令的值,拿到name的值,触发一次this.get( )
    添加订阅者 -> Dep.target = this; 这个过程中触发defineProperty get( )建立 dep和watch的关系。
    watch里的this.depIds ={ } 把dep 压入进去, dep里的 this.subs=[ ] 把watch放进去
    一个watch里面只能有一个dep, 一个dep里面可以有多个watch (dep其实就是可动态更改的对象数目)
    (watch是模板指令数目)对象在模板上可以对应多个指令进行模板修改。
  2. 监听属性,Observer 给每个对象变量进行劫持。并初始化Dep对象,以便进行闭包调用
  3. 当method通过addEventListener触发是,Observer监测到对象有变化,在set( )方法里调用Dep的notify方法,对保存在Dep里的Watch进行迭代,调用Watch里的update方法,update方法拿到新set的对象的值,对比老对象的值,调用各个指令节点绑定的方法,进行指令替换。

SSR服务端渲染

link

Vue在项目中的使用

(下面全部搞懂了,Vue基本就会用了。)

  • Vue模板中所有的数据字段由数据库接管。

  • Vue数据流:

    1
    2
    3
    4
    5
    {
    单页面组件数据,data(){ this.data=..} 用来写入template。
    props父亲传子组件数据,父子数据传递
    Vuex全局组件共享数据源,记录APP共享的状态机
    }
  • Vue生命周期:

  • Vue路由对象使用 VueRouter
  • VueLoader(载入程序) js, scss sass,css,less typescript转换工具
  • Vue服务端渲染(未知)

VueX 在项目里是怎么用的?

import {mapState, mapActions} from 'vuex'
export default {
    data(){
        return{
        }
    },
    mounted(){
        //获取用户信息
        this.getUserInfo();
    },
    props: ['signinUp', 'headTitle', 'goBack'],
    computed: {
        ...mapState([
            'userInfo'
        ]),
          //映射this.userInfo 为 store.state.userInfo
        //this.userInfo = store.state.userInfo;
        //this.userInfo没有在data里声明,template也可以直接拿来用。
    },
    methods: {
        ...mapActions([
            'getUserInfo'
        ]),
        //this.getUserInfo没有声明,可以直接拿来用。
    },
}

webpackBootstrap

link
link


其他

  • px rem的区别 https://www.cnblogs.com/huanghuali/p/6931080.html
  • 在根元素中定义了一个基本字体大小为62.5%(也就是10px。设置这个值主要方便计算,如果没有
    设置,将是以“16px”为基准 );对于只需要适配少部分手机设备,且分辨率对页面影响不大的,使用px即可
    对于需要适配各种移动设备,使用rem,例如只需要适配iPhone和iPad等分辨率差别比较挺大的设备
  • v-bind 用来操作html原生属性 https://blog.csdn.net/gao_xu_520/article/details/76172406

  • Vue props用法小结 https://blog.csdn.net/q3254421/article/details/84068341 父组件用来给子组件传值,可以用到props

  • export 和 default export的区别 https://www.jianshu.com/p/edaf43e9384f

1
2
<style lang="scss" scoped> 
只对当前页面有效。

2017-03-15

填充的是路由的模板内容
$ref 获取组件内的元素,
$parent:获取当前组件对象的父组件
$children:获取子组件
$root: 获取new Vew的实例
$el:组件对象的DOM元素

1
2
3
4
5
6
7
VueRouter -> 

window.addEventListener(‘hashchange’,function( ){
switch(location.hash)
case ‘#login’:
div.innerHTML=“<h1>登陆页面</h1>"
})

Vue.$nextTick 获取更新后的 DOM,在 DOM 更新循环结束之后执行延迟回调


2017-03-14

Vue对象生命周期

只能和 配套使用。

Vue内置自定义组件。 父组件传递的DOM结构。

computed:凡是函数内部有this.相关属性,改变都会触发当前函数,监视多个
watch: 监视单个


Vue和原生,jQueryAPI替代

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//$选择器可以用ref替代
<section class="menu_right" ref="menuFoodList"></section>
const listContainer = this.$refs.menuFoodList;

$(elem).find('.activity_menu') //替代
this.$refs.wrapperMenu.querySelectorAll('.activity_menu')

$.each //替代
listContainer.forEach((item, index) => {
this.shopListTop[index] = item.offsetTop;
});

v-bind -> setAttribute()
v-text -> innerText
v-html -> innerHTML
v-if ->
parent.removeChild("elem")
parent.append("ssss")
v-show ->
obj.style.display='none/block'
v-on: ->
addEventListener (简写成) @
v-model ->
双向数据流绑定,只能给具有value属性的元素进行双向数据绑定(必须使用的具有value属性的元素)
v-model实现自定义的表单组件:
<currency-input v-model="price"></currency-input>

监听原生组件的事件,当获取到原生组件的值后把 值通过调用 $emit(‘input’ ,data) 方法去触发 input事件

v-bind 可以给任何赋值属性,是从value到页面的单向数据流 简写成 :title

1
2
3
4
5
6
7
<li v-for="item in items”>   
{{ item.message }}
</li>

$.each(items,funciton(i,obj){
$(elem).parent().append("<li>...</li>")
})

v-style :style -> $.addClassRule

props用在定义自定义标签组件的属性上,v-bind用在定义原生标签属性上。

1
2
3
4
5
6
7
8
<span v-text="myText"></span> 
<hr />
<span v-html="myHtml"></span>

<button v-if="isExit"></button>
<button v-if="num==1">测试Num1</button>
<button v-else-if="num==2">测试Num2</button>
<button v-show="isShow">v-show<button>

v-bind是 一个vue 指令,用于绑定 html 属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div id="app”> 
<p v-bind:title="t1 + ' ' + t2">
html属性不能使用双大括号形式绑定,只能使用v-bind指令
</p>
可简写成
<p :title="t1+' '+t2"></p>
</div> ......
var vm = new Vue({
el: '#app',
data: {
t1: 'title1',
t2: 'title2'
}
});

前端知识点

Posted on 2017-03-24 | Edited on 2019-06-02

OS

缓存

1
2
3
cache狭义指的是CPU和RAM主存之间的Cache(利用比较昂贵的SRAM) 而且在内存和硬盘之间也有Cache(磁盘缓存)
乃至在硬盘与网络之间也有某种意义上的Cache(如浏览器缓存) 当然也有代码级缓存(某些斐波那契算法)
(内存包括ROM RAM Cache)

ASCII

1
2
3
4
5
6
7
8
9
10
11
12
13
ASCII = 美国信息交换标准代码

标准: 使用7位二进制数来表示所有的 大写/小写字母 数字0-9 标点符号 美式英语特殊控制字符

0 - NUL空字符
48 - 数字0
65 - A
97 - a

其最高位(b7) == 奇偶校验位:
所谓奇偶校验 是指在代码传送过程中用来检验是否出现错误的一种方法 一般分奇校验和偶校验两种。
奇校验规定: 正确的代码一个字节中1的个数必须是奇数,若非奇数,则在最高位b7添1
偶校验规定: 正确的代码一个字节中1的个数必须是偶数,若非偶数,则在最高位b7添1

unicode

1
2
Unicode=国际码=国际字符和二进制数字的对应关系
Unicode 只是一个符号集 它只规定了符号的二进制代码 却没有规定这个二进制代码应该如何存储

UTF-8

1
UTF-8 就是在互联网上使用最广的一种 Unicode 的实现方式。
1
UTF-8 最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
1
2
3
4
5
6
7
8
9
10
11
12
UTF-8 的编码规则很简单 只有二条:

1.对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。
2.对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。

//下面,还是以汉字严为例,演示如何实现 UTF-8 编码。

//严的 Unicode 是4E25(100111000100101)
//根据上表,可以发现4E25处在第三行的范围内(0000 0800 - 0000 FFFF)
//因此严的 UTF-8 编码需要三个字节,即格式是1110xxxx 10xxxxxx 10xxxxxx。
//然后,从严的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。
//这样就得到了,严的 UTF-8 编码是11100100 10111000 10100101,转换成十六进制就是E4B8A5。

URL 编码

1
2
3
4
5
URL编码 == 百分号编码

对以下类型字符进行编码:
1.引起歧义的字符 (如 value 字符串中包含了 = 或者 & 比附宝洁公司的简称为P&G)
2.非法字符 (URL 的编码格式采用的是 ASCII 码 而不是 Unicode 这也就是说你不能在 URL 中包含任何非 ASCII 字符 例如中文)

网络

HTTP

使用同一个[TCP连接]来发送和接收多个[HTTP请求/应答]

1
2
3
HTTP 1.0 => 如果浏览器支持 keep-alive => 请求头中添加 Connection: Keep-Alive => 服务器收到请求添加 Connection: Keep-Alive => 保持连接

HTTP 1.1 所有的连接默认都是持续连接 除非特殊声明不支持
1
2
3
4
HTTP 2.0 不会线头阻塞 同时通过单一的 HTTP/2 连接发起多重的请求-响应消息
HTTP 2.0 server push 技术 服务器可以对客户端的一个请求发送多个响应
+首部压缩
+二进制分帧层

CGI & WSGI

CGI: 通用网关接口 连接web服务器和应用程序的接口
WSGI: Python的CGI包装

内网 IP 网段

1
2
3
4
5
但是在IPv4地址协议中预留了3个IP地址段,作为私有地址,供组织机构内部使用。
这三个地址段分别位于A、B、C三类地址内:
A类地址:10.0.0.0--10.255.255.255
B类地址:172.16.0.0--172.31.255.255
C类地址:192.168.0.0--192.168.255.255

http 状态码

1
2
3
4
5
6
7
8
9
10
11
12
13
1**(信息类):表示接收到请求并且继续处理

2**(响应成功):表示动作被成功接收、理解和接受

3**(重定向类):为了完成指定的动作,必须接受进一步处理
301——本网页被永久性转移到另一个URL
302——请求的网页被转移到一个新的地址。
...
304——自从上次请求后,请求的网页未修改过,服务器返回此响应时,不会返回网页内容,代表上次的文档已经被缓存了,还可以继续使用

4**(客户端错误类):请求包含错误语法或不能正确执行

5**(服务端错误类):服务器不能正确执行一个正确的请求

四种 POST 编码方式

1
2
服务端通常是根据请求头(headers)中的 Content-Type 字段来获知请求中的消息主体是用何种方式编码
再对主体进行解析。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1.application/x-www-form-urlencoded

提交的数据按照 key1=val1&key2=val2 的方式进行编码,key 和 val 都进行了 URL 转码。


2.multipart/form-data

生成了一个 boundary 用于分割不同的字段


3.application/json

默认提交 JSON 字符串


4.text/xml

使用 XML 作为编码方式的远程调用规范

DNS污染

1
2
3
4
5
6
DNS污染主要原理:
用户在访问/请求一个网址时,会将域名提交给DNS来查询这个域名的IP,
查询到域名的IP后才会去访问这个IP请求到想要的资源。
GFW的五种封锁方法中,最简单粗暴的就是DNS污染,
DNS污染的主要原理就是干扰查询域名的返回IP。
而hosts文件优先级比DNS高,hosts文件里若有对应IP,会使计算机绕过DNS直接访问IP。

补充:绕过DNS后如果遇到了IP封锁仍然会访问失败。

NAT

NAT=网络地址转换(Network Address Translation)
wifi=内网ip->公网ip->请求主机

浏览器

浏览器的5个常驻线程

1
2
3
4
5
6
7
8
9
10
11
12
13
浏览器的五个常驻线程:

1.浏览器 GUI 渲染线程

2.javascript 引擎线程

3.浏览器定时器触发线程( setTimeout,setInterval )

4.浏览器事件触发线程

5.浏览器 http 异步请求

当js引擎线程(第二个)进行时,会挂起其他一切线程,这个时候3、4、5这三类线程也会产生不同的异步事件,由于 javascript引擎线程为单线程,所以代码都是先压到队列,采用先进先出的方式运行,事件处理函数,timer函数也会压在队列中,不断的从队头取出事件,这就叫:javascript-event-loop。简单点说应该是当在进行第二线程的时候,1,3,4,5都会挂起,比如这时候触发click事件,即使先前JS已经加载完成,click事件会压在队列里,这里也要先完成第二线程才会执行click事件。

OPTIONS方法

区别于GET, POST, DELETE, PUT

1
2
3
4
5
6
7
8
9
跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站有权限访问哪些资源。


特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求 比如 Content-Type application/json 的请求:
浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。
服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。

ps: 某些请求不会触发 CORS 预检请求 我们称之为 简单请求
ps: 对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段(详见下文),就知道出错了,从而抛出一个错误,被XMLHttpRequest的onerror回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。

如何减少OPTIONS预检频率? 答案:Access-Control-Max-Age: 86400

输入URL按回车后都发生了什么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
-DNS解析 (查询 DNS 浏览器缓存,系统缓存,路由器缓存,IPS服务器缓存,根域名服务器缓存,顶级域名服务器缓存,主域名服务器缓存) -> 

-TCP连接 (
// 3次握手
// 第一次握手 客户端发送 SYN=> 服务端 (客户端: 我来了)
// 第二次握手 服务端发送 ACK=> 客户端 (服务端: 好嘞)
// 第三次握手 客户端发送 ACK=> 服务端 (客户端: 好嘞)

// 4次挥手
// 第一次挥手 主动关闭方 FIN=> 被动关闭方 (客户端: 该撤了)
// 第二次挥手 被动关闭方 ACK=> 主动关闭发 (服务端: 好嘞)
// 第三次挥手 被动关闭方 FIN=> 主动关闭发 (服务端: 你撤那我也得撤啊 都确认了你最后再吱一声 不然我一直等着就智障了)
// 第四次挥手 主动关闭方 ACK=> 被动关闭方 (客户端: 好勒)
) ->

-( SSL 四次握手 hello -> certificate-hello -> exchangekey -> finished )

-发送HTTP请求 (HTTP请求报文是由三部分组成: 请求行, 请求报头和请求正文) ->

-服务器处理请求返回HTTP报文 (查找资源返回状态码) ->

-浏览器解析渲染页面 (
// T0 -> [Get HTML] => [Browser Parsing HTML & prefetch]
// T1.0 -> [Parsing Dom (Will Parse All The Tags Before Script Soon)]
// T1.1 -> (Maybe CSS Recieved / JS Recieved) => [(Dom Render Blocking) Load CSS & Build CSSOM] => [Combine CSSOM + DOM -> Render Tree] => [Paint]
// T1.2 -> [(Dom Parser Blocking) Load JS] => [Run JS] => [Parsing Dom] => (DomContentLoaded Event)
// T1.3 -> [Composite Layers] => (Load Event)
) ->
连接结束

浏览器渲染流程

1
2
// T0
浏览器预下载: 现代浏览器很聪明,会进行 prefetch 优化,浏览器在获得 HTML 文档之后会对页面上引用的资源进行提前下载。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// T1.0
DOM解析: 解析器把 HTML 转换为 JavaScript 可存取的 Dom 对象 (由浏览器 UI Render 线程负责)
// T1.1
CSS加载: CSS加载会阻塞 DOM 渲染,不会阻塞 DOM 解析。CSS是阻塞渲染的资源。需要将它尽早、尽快地下载到客户端,以便缩短首次渲染的时间。
Render树: CSSOM 树和 DOM 树合并成渲染树,然后用于计算每个可见元素的布局,并输出给绘制流程,将像素渲染到屏幕上。
// T1.2
JavaScript加载: JavaScript可以查询和修改 DOM 与 CSSOM,所以 JavaScript 执行会阻止 DOM 和 CSSOM 构建 (除非将 JavaScript 显式声明为异步执行)。需要尽量将JavaScript放在 body 的末尾,保证首屏页面先渲染。V8 引擎执行 JavaScript 代码。
DOMContentLoaded Event: 浏览器已经完全加载了HTML,DOM树已经构建完毕,但是像是 <img> 和样式表等外部资源可能并没有下载完毕。
// T1.3
Load Event: 浏览器已经加载了所有的资源(图像,样式表等)。

// Paint 问题
// 经过 Chrome Performance 试验结果:
// 正常情况下 CSS Loaded + Parse StyleSheet (Build CSSOM) 随后会 Paint (即 T1.1 中的 Paint)
// 个别情况下 如 JS 资源下载不耗时 原先 T1.1 中的 Paint 可能会延后到 T1.2 DomContentLoaded Event 之后

Reflow 和 Repaint

1
2
3
4
5
6
7
8
9
回流: reflow, 浏览器得知元素产生了对文档树排版有影响的样式变化,对所有受影响的dom节点进行重新排版工作
重绘: repaint,就是浏览器得知元素产生了不影响排版的情况下后对这个元素进行重新绘制的过程。例如我们改变了元素的颜色,加个下划线等。

优化思路:
reflow一定触发repaint
repaint不一定触发reflow
因为reflow开销远大于repaint
所以尽量少去触及dom节点重新排版的工作
有时可以直接把改变的元素absolute踢出文档流 reflow+repaint即只涉及这一个节点

浏览器缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1.通过 Meta 标签控制: 
例如 - <META HTTP-EQUIV="Pragma" CONTENT="no-cache">
只有部分浏览器支持 并为广泛使用

2.通过 HTTP 缓存控制:
-2.1 强缓存:
Expires 是服务器端在响应请求时用来规定资源的失效时间。
Cache-Control (HTTP 1.1 出现) 是服务器端在响应请求时用来规定资源是否需要被浏览器缓存以及缓存的有效时间等。

Cache-Control 的优先级要高于 Expires

-2.2 协商缓存:
Etag ETag可以保证每一个资源是唯一的,资源变化都会导致ETag变化。
Last-Modify 该资源的最后修改时间

服务器会优先验证ETag

各大浏览器厂商内核

IE4-9: Trident
IE10+: EdgeHTML
FireFox: Gecko
Opera: Presto
Safari: Webkit
Chrome: Webkit

Tap 与 Click

1
2
3
4
5
6
7
触屏网页的 tap 与 click :
两者都会在点击时触发,但是在手机WEB端,click会有 200~300 ms,所以请用tap代替click作为点击事件
//(触屏穿透问题最好通过300ms隐形浮层方法解决 或者 fastclick.js)

tap 实现思路:
基于三个基础触摸事件: touchstart、touchmove、touchend
// 滑动则不触发 tap + 长按超时则不触发 tap

LocalStorage SessionStorage 两页面通信

1
2
LocalStorage 同源则 => 可以
SessionStorage 同源也 => 不可以

同源定义

1
[协议]+[端口]+[域名] 都相同

JavaScript

ES6 ES7 ES8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
ES6常用新特性:

1.let && const

2.iterable类型
/**
*
let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();

iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }
*/

3.for of
// 用于遍历数组 与forEach()不同的是 它可以正确响应 break continue return 语句

4.解构赋值
// let [a, b, c] = [1, 2, 3];

5.箭头函数
// =>

6....操作符
// Math.max(...[14, 3, 77]) 等同于 Math.max(14, 3, 77);

7.类
// ES6 的 class 可以看作只是一个语法糖
// 类的方法内部如果含有 this 它默认指向类的实例
// 如果一定要实现私有方法可以通过 Symbol 变通实现
const bar = Symbol('bar');
const snaf = Symbol('snaf');

export default class myClass{

// 公有方法
foo(baz) {
this[bar](baz);
}

// 私有方法
[bar](baz) {
return this[snaf] = baz;
}

// ...
};


ES7新特性:

1.Array.prototype.includes

2.Exponentiation Operator(求幂运算)


ES8新特性:
1.Object.values + Object.entries
// let obj = { a: 1, b: 2, c: 3 };
// Object.values(obj) [1,2,3];
// Object.entries(obj) ['a', 1], ['b', 2], ['c', 3]

2.String padding
// 字符串填充

3.Object.getOwnPropertyDescriptors
// writable 属性的值是否可以被重写
// enumerable 此属性是否可以被枚举
// configurable 是否可以删除目标属性或是否可以再次修改属性的特

4.函数参数列表和调用中的尾逗号
// Trailing commas

5.异步函数
// Async Functions

throttle & debounce

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// throttle 节流
// 例如用户8s内连续点击300次按钮 可以throttle(fn, 5000)实现节流5s内只触发一次
module.exports = function throttle (fn, time) {

var that = fn,
timer,
firstTime = true;

return function () {
let args = arguments,
me = this;

if (firstTime) {
fn.apply(me, args)
return firstTime = false
}
if (timer) {
return false
}

timer = setTimeout(function() {
clearTimeout(timer)
timer = null
fn.apply(me, args)
}, time || 500)
}
}



// debounce 防抖
// 例如搜索框只有在用户停止输入1s后才出现联想词 deboune(fn, 1000)
module.exports = function debounce (fn, delay) {
var timer = null;

return function () {
clearTimeout(timer);
timer = setTimeout( function () {
fn();
}, delay);
}
}

ES6的模块化

1
2
ES6的模块化的基本规则或特点
1:每一个模块只加载一次, 每一个JS只执行一次, 如果下次再去加载同目录下同文件,直接从内存中读取。 一个模块就是一个单例,或者说就是一个对象;

ES6 import

1
2
3
4
import 命令会被 JavaScript 引擎静态分析 先于模块内的其他语句执行

如果 import 命令要取代 Node 的 require 方法 这就形成了一个障碍
因为 require 是运行时加载模块 import 命令无法取代 require 的动态加载功能

symbol

1
Symbol可以在set对象property的时候保证不会发生覆盖

Array.prototype.sort

1
2
3
4
5
6
7
8
9
10
//错误用法
//会根据首个char字符assic码排序
Array.prototype.sort.call([12,1,5])
>>> (3) [1, 12, 5]

//正确用法
Array.prototype.sort.call([12,1,5],(a,b)=>(a-b))
>>> (3) [1, 5, 12]

使用Array.prototype.sort不应该只返回两种值 true false 相当于 1 0 而没有 -1

JavaScript This

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
1.纯粹的函数调用: this === 全局对象 
console.log(this === window) // true

2.作为对象方法的调用: 这时 this 就指这个上级对象
x = {
a: 1,
f: function() { console.log(this.a) }
}
x.f() //1

3.作为构造函数调用: this 指向这个新对象
function o(n) { this.a = n }
let o1 = new o(1) // {a: 1}

4.call/apply调用: 指向显式调用的对象
x = {
a: 1,
f: function() { console.log(this.a) }
}
x.f.call({a:2}) //2

5.箭头函数: 箭头函数不会创建自己的 this 它只会从自己的作用域链的上一层继承 this
x = {
a: 1,
f: () => { console.log(this === window) }
}
x.f() //true

Event Loop机制

1
2
3
4
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。

macrostasks和microtasks

1
2
3
4
除了广义的同步任务和异步任务,我们对任务有更精细的定义:

macro-task(宏任务):包括整体代码script,setTimeout,setInterval
micro-task(微任务):Promise,process.nextTick
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
setTimeout(() => {console.log(1)}, 0);
new Promise((resolve, reject) => {
resolve('ok')
}).then(function() {
console.log(2);
}).then(function() {
console.log(3);
}).then(function() {
console.log(4);
});
console.log(5);

// >> 5 2 3 4 1
// 主线程的所有代码执行结束后。先从微任务queue里拿回掉函数,然后微任务queue空了后再从宏任务的queue拿函数。
// 事件循环总是 一个宏任务 => 所有微任务 => 一个宏任务 => 所有微任务 ...

setImmediate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
console.log(0);
setImmediate(() => {
console.log(1)
}, 0)
setTimeout(() => {
console.log(2)
}, 0)
new Promise((resolve, reject) => {
resolve('ok')
}).then(() => {
console.log(3);
})
setTimeout(() => {
console.log(4)
}, 1000)
process.nextTick(() => {
console.log(5)
})
console.log(6)

// 0 主执行栈先执行
// 6 主执行栈先执行
// 5 process.nextTick会在事件队列中强势插入
// 3 正常macrotasks执行后该microtasks执行
// 2
// 1 同一时刻setImmediate压入事件队列的顺序会比setTimeout优先级低
// 4

原型 与 prototype

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
function DOG(name){
this.name = name;
}


DOG.prototype.species = '犬科'; // 尽量不要用改变 prototype 引用的写法 例如: DOG.prototype = { species: '犬科' } ...

var A = new DOG('大毛');
var B = new DOG('二毛');

console.log(A.species); // 犬科
console.log(B.species); // 犬科

DOG.prototype.species = '猫科';

console.log(A.species); // 猫科
console.log(B.species); // 猫科

// 原理: 实例会沿着原型链向上寻找species属性





/**

@ 引用关系图
----.constructor-->
A ---.__proto__---> DOG.prototype ( ) DOG
<----.prototype----

**/

console.log(A.__proto__);
// {species: "猫科", constructor: ƒ}
// 其实就是 DOG.prototype

console.log(DOG.prototype, 'is equal ?', A.__proto__ === DOG.prototype);
// {species: "猫科", constructor: ƒ} 'is equal ?' true
// 就是他

console.log(DOG.prototype === DOG.prototype.constructor.prototype);
// true
// 互相引用



/** @ DOG 和 Function 的关系 **/

console.log(DOG.__proto__);
// ƒ () { [native code] }
// 其实就是 Function.prototype

console.log(Function.prototype, 'is equal ?', DOG.__proto__ === Function.prototype);
// ƒ () { [native code] } "is equal ?" true
// 同理

console.log(Function.prototype === Function.prototype.constructor.prototype)
// true
// 同理




/** @ 所以 对象字面量也同理 **/

let C = { name: "柴犬" };
console.log(C.__proto__ === Object.prototype);
// true

console.log(Object.prototype === Object.prototype.constructor.prototype);
// true

实现简易 Promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Promise 是包含 pending fufilled rejected 三种状态的状态机 开发者可以指定未来发生的事件

function prototypePromise (excuteFunction) {
this.stack = [];
excuteFunction(this.onResolve.bind(this), this.onReject.bind(this));
}



prototypePromise.prototype.then = function (onResolve) {
this.stack.push(onResolve);
return this;
}

prototypePromise.prototype.catch = function (handleError) {
this.handleError = handleError;
return this;
}

prototypePromise.prototype.onResolve = function (value) {
let closer = value;
try {
Array.prototype.forEach.call(this.stack, (next) => {
closer = next(closer);
});
}
catch (e) {
this.onReject(e)
}
}

prototypePromise.prototype.onReject = function (err) {
this.handleError(err)
}

JavaScript内存回收

1.标记清除: 变量离开执行环境就收回
2.引用计数: 简单粗暴 会受循环引用影响//(中虽然JavaScript对象通过标记清除的方式进行垃圾回收,但BOM与DOM对象却是通过引用计数回收垃圾的, 也就是说只要涉及BOM及DOM就会出现循环引用问题)

js的[按值传递]与[按引用传递]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

六种基本数据类型

undefined
null
string
boolean
number
symbol(ES6)



一种引用类型

Object
1
2
3
4
5
6
//本质原理
原始数据类型 => 存储在栈中
//占据空间固定
引用类型 => 存储在堆中
//(而存储在变量处的值实际上是指针point,指向存储对象内存地址)
//占据空间大小会变

typeof 对照表

1
2
3
4
5
6
7
8
9
typeof(undefined); // undefined
typeof(null); // object ☆
typeof(true); // boolean
typeof(3); // number
typeof('str'); // string
typeof(Symbol('k')); // symbol
typeof({key: 0}); // object
typeof(function(){}); // function
typeof([1, 2, 3]); // object ☆

判断是否 Array

1
2
3
4
5
6
7
let arr = [1, 2, 3]

console.log(arr instanceof Array); // true // es6

Object.prototype.toString.call(arr) === '[object Array]'; // true

arr.constructor === Array; // true

判断是否空对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let obj = {};

// 直接 for in + hasOwnProperty
function ieo (obj) {
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
return false;
}
}
return true;
}



ieo(obj); // true

JSON.stringify(obj) === '{}'; // true

Object.keys(obj).length === 0; // true // es6

如何正确遍历对象属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Object.prototype.c = 1; // 把属性c放在 Object 原型链上
let k = { a: 2 }; // 通过字面量创建一个 Object 实例

// $ 错误方法: for in
for (var i in k) {
console.log(i) // a, c // 会把原型链上的属性也打出来
}

// $ 正确方法: 1.Object.keys
Object.keys(k); // ['a']

// $ 正确方法: 2.hasOwnProperty
k.hasOwnProperty('a') // true
k.hasOwnProperty('c') // false

箭头函数

1
2
3
4
5
6
7
8
9
10
11
12
1.箭头函数不会创建自己的this 只会从自己的作用域链的上一层继承this ☆

2.通过 call() 或 apply() 方法调用一个函数时 第一个参数会被忽略 (bind也同样成立)

3.不绑定Arguments对象

4.不能和new一起使用 和new一起用会抛出错误
// 因为使用箭头函数后this会指定闭合的当前上下文,而当函数做为构造器的时候,this又会指向生成的实例, 这个造成歧义。

5.没有prototype

6.不能使用yield关键字

XSS

1
2
3
4
5
XSS: 跨站脚本攻击
恶意web用户将代码植入到提供给其它用户使用的页面中

XSS防范:
永远不要相信用户的输入 后端转义 + 前端转义

CSRF

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CSRF: 跨站请求伪造
一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法

CSRF 防范

1.最基础的防范就是在 Get 与 Post 区别的文章中有说过
Post 请求要比 Get 更为安全 但只能说会 Pass 一些低级攻击选手。

2.请求来源限制——验证 HTTP Referer 字段
在 HTTP 请求头中有一个字段叫 Referer 它记录了请求的来源地址
服务器需要做的是验证这个来源地址是否合法 如果是来自一些不受信任的网站 则拒绝响应
(但是 Refer 也能伪造)

1. CSRFToken
使用随机数作为请求参数来校验

前端路由原理

1
2
hash模式: [hash 改变] + [监听 hashChange] + [route map 执行回调]
history模式: [主动 pushState 改变 Path] + [route map 执行回调]

CSS3旋转

1
2
3
4
5
-webkit-animation: ani-load 0.2s linear infinite;
animation: ani-load 0.3s linear infinite;
//
@-webkit-keyframes ani-load{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}
100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}

CSS3 transform

1
transform: rotate(-25deg) translateX(30px); // 这种先rotate的写法 会把整个坐标系先旋转-25°再平移30px 也就是会向斜上方平移

box-sizing

1
2
3
4
5
6
7
8
9
10
11
<div class="wrap-a">
<p>a</p>
</div>
<div class="wrap-b">
<p>b</p>
</div>
<!-- a和b的border-box等价-都能实现占据一行自适应宽度留空间-->
<!-- 然而实现占据一半自适应宽度留空间 c的border-box是最佳方法-->
<div class="wrap-c">
<p>c</p>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.wrap-a {
padding: 20px;
background-color: #CCC;
}
.wrap-b {
width: 100%;
padding: 20px;
box-sizing: border-box;
background-color: #777;
}
.wrap-c {
width: 50%;
padding: 20px;
box-sizing: border-box;
background-color: #EEE;
}
p {
background-color: #444;
}

圣杯布局

经典圣杯

1
2
3
4
5
<div class="wrap">
<div class="center"></div>
<div class="left"></div>
<div class="right"></div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.wrap {
padding: 20px;
}
.wrap div {
float: left;
height: 30px;
position: relative;
}
.wrap .left {
width: 20px;
left: -20px;
margin-left: -100%;
}
.wrap .center {
width: 100%;
}
.wrap .right {
width: 20px;
margin-right: -20px;
background-color: blue;
}

Flex圣杯

1
2
3
4
5
<div class="container">
<div class="left"></div>
<div class="main"></div>
<div class="right"></div>
</div>
1
2
3
4
5
6
7
8
9
10
.container{
display: flex;
height: 100px;
}
.container .left, .container .right{
flex: 0 0 40px;
}
.container .main{
flex: 1;
}

一线天

中间固定-两端自适应(PC打开移动页面)

1
2
3
4
5
<div class="container">
<div id="left"></div>
<div id="main"></div>
<div id="right"></div>
</div>
1
2
3
4
5
6
7
8
9
10
#left, #right {
float: left;
width: 50%;
margin: 0 0 0 -70px;
}

#main {
width: 140px;
float: left;
}

Flex 一线天

1
2
3
4
5
<div class="container">
<div id="left"></div>
<div id="main"></div>
<div id="right"></div>
</div>
1
2
3
4
5
6
7
8
9
10
11
.container {
display: flex;
}

#left, #right {
flex: 1;
}

#main {
flex: 0 0 140px;
}

绝对居中

经典居中

1
2
3
4
5
6
7
8
9
10
11
.container {
display: relative;
}
.container div {
position: absolute; /* 相对定位或绝对定位均可 */
width: 500px;
height: 300px;
top: 50%;
left: 50%;
margin: -150px 0 0 -250px; /* 外边距为自身宽高的一半 */
}

transform居中

1
2
3
4
5
6
7
8
9
.container {
display: relative;
}
.container div {
position: absolute; /* 相对定位或绝对定位均可 */
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}

flex居中

1
2
3
4
5
6
7
.container {
display: flex;
align-items: center; /* 垂直居中 */
justify-content: center; /* 水平居中 */

}
.container div {}

flex property

1
2
3
4
5
6
7
8
9
10
11
flex: flex-grow flex-shrink flex-basis|auto|initial|inherit;

/*
flex-grow 一个数字,规定项目将相对于其他灵活的项目进行扩展的量。
flex-shrink 一个数字,规定项目将相对于其他灵活的项目进行收缩的量。
flex-basis 项目的长度。合法值:"auto"、"inherit" 或一个后跟 "%"、"px"、"em" 或任何其他长度单位的数字。
auto 与 1 1 auto 相同。
none 与 0 0 auto 相同。
initial 设置该属性为它的默认值,即为 0 1 auto。请参阅 initial。
inherit 从父元素继承该属性。请参阅 inherit。
*/

jsonp

1
2
3
4
为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP。   
该协议的一个要点就是允许用户传递一个callback参数给服务端
然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据
这样客户端就可以随意定制自己的函数来自动处理返回数据了。
1
2
3
4
5
6
误解: jQuery Zepto之类的库把Ajax方法和jsonp方法封装在一起是容易产生误解的。   
jsonp: jsonp实际上就不是ajax方法
jsonp利用<script>标签可以跨域的历史漏洞实现跨域请求参数
服务端将callback参数作为一个js函数的函数名而回调数据作为函数参数返回

callback({ msg:'this is json data'})//jsonp方法的回调

多核处理上 node 如何榨干处理器资源?

1
2
3
4
5
6
=> 多进程

// 为了将多核 CPU 的性能发挥到极致
// 最大程度地榨干服务器资源
// egg 采用多进程模型
// 解决了一个 Node.js 进程只能运行在一个 CPU 上的问题

Express 原理

1
Express 有几个比较重要的概念: 中间件 路由 模版引擎

Express 中间件原理

1
2
3
4
5
6
7
8
9
10
app.use:
加载用于处理 http 请求的 middleware 中间件

函数数组:
express 内部维护一个函数数组 这个函数数组表示在发出响应之前要执行的所有函数

结论:
使用 app.use(fn) 后 传进来的fn就会被扔到这个数组里
执行完毕后调用 next() 方法执行函数数组里的下一个函数
如果没有调用 next() 的话 调用就会被终止

Express 路由原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
router对象的主要作用:
创建一个普通中间件 或者 路由中间件的引导着(这个引导着Layer对象链接到一个route对象)
然后将其保存到自己的stack数组中

var express = require('express');
var app = express();

// 没有挂载路径的中间件,应用的每个请求都会执行该中间件
app.use(function (req, res, next) {
console.log('Time:', Date.now());
next();
});

// 挂载至 /user/:id 的中间件,任何指向 /user/:id 的请求都会执行它
app.use('/user/:id', function (req, res, next) {
console.log('Request Type:', req.method);
next();
});

// 路由和句柄函数(中间件系统),处理指向 /user/:id 的 GET 请求
app.get('/user/:id', function (req, res, next) {
res.send('USER');
});


var server = app.listen(3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log('Example app listening at http://%s:%s', host, port);
});

Express 模板引擎原理

1
ejs / jade 将数据和模板整合最终生成 html 文件

JavaScript 谷歌规范

[1]声明式和表达式规范

结构:应符合[变量-业务逻辑-函数声明]的结构规范

[函数声明式]与[函数表达式]在js解释器预编译阶段存在区别

[函数声明式]函数声明和他的赋值都会被提前

1
2
3
4
5
6
7

a();// >> 1

//声明式
function a(){
return 1;
}

[函数表达式]和变量一样[声明被提前]而[赋值并不被提前]

1
2
3
4
5
6
7
8
9

console.log(e);// >> undefined

e();//ERROR:e is not a function(…)

//表达式
var e =function(){
return 0;
}

结构:应符合[变量-业务逻辑-函数声明]的结构规范

[2]注释规范

注释:用醒目多行注释对函数表达式的[参数类型]和[返回值类型]进行注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

/**
* 返回字符的字节长度(汉字算2个字节)
* @param {string}
* @returns {number}
*/

var getByteLen = function(val){
var len = 0;
for (var i = 0; i < val.length; i++) {
if (val[i].match(/[^x00-xff]/ig) != null) //全角
len += 2;
else
len += 1;
};
return len;
}

Objective-C

iOS中文键盘滚动

解决iOS中文键盘滚动的遮挡问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#import "ViewController.h"
#import <WebKit/WebKit.h>
#import <objc/runtime.h>

@interface ViewController ()
@property(strong,nonatomic) WKWebView *webView;
@end

@implementation ViewController


- (void)viewDidLoad {
[super viewDidLoad];

_webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 60, 320, 508)];
_webView.navigationDelegate = self;
_webView.scrollView.bounces= NO;
NSURL *nsurl=[NSURL URLWithString:@"https://XXXXXX..."];

NSURLRequest *nsrequest=[NSURLRequest requestWithURL:nsurl];
[_webView loadRequest:nsrequest];
[self.view addSubview:_webView];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(boardWillShow:) name:UIKeyboardWillShowNotification object:nil];

}

-(void)boardWillShow:(NSNotification *)sender{
//获得键盘的尺寸
CGRect keyBoardRect=[sender.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
_webView.scrollView.contentOffset = CGPointMake(0, keyBoardRect.size.height);
}



- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}



@end

iOS存储区

iOS存储区

1
2
3
4
5
6
7
8
9
10
11
Documents/  
//使用这个目录来保存用户生成的文件。用户可以通过文件分享功能访问这个目录的内容。因此,这个目录你应该只放一些你希望展示给用户的文件。这个目录的文件会被 iTunes 和 iCloud 备份。


Library/ (后来发现在wkwebview下会有坑)
//使用 Library 子文件夹来存放任何你不想被用户看到的文件。你的app不应该使用这些文件夹来存放用户数据文件。
//除了 Caches 子文件夹,Library 文件夹下的内容会被 iTunes 和 iCloud 备份。

tmp/
//使用这个文件夹来存放在临时文件,系统也可能会在你的 app 不再运行的时候清除这个文件夹。
//这个目录的文件 不会 被 iTunes 和 iCloud 备份。

算法

JavaScript 类似[列表生成式]版本的[快排]

1
2
3
4
5
6
7
8
9
10
11
12
13
function qsort(arr) {
if (arr.length <= 1) { return arr; }
var pivot = arr[0];
var left = []
var right = [];
for (var i = 1; i < arr.length; i++) {
if (arr[i] < pivot) {left.push(arr[i])}
else {right.push(arr[i])}
}
return qsort(left).concat([pivot], qsort(right));
}

alert(qsort([32,45,37,16,2,87]));//弹出“2,16,32,37,45,87”

JavaScript [空间最优]版本的[快排]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Array.prototype.copy = function() {
return this.constructor.apply(this,this)
}

function qsort (list) {
if (list.length <= 1) {
return list
}
let copyList = list.copy();
partial(copyList, 0, copyList.length - 1);
return copyList;
}

function partial (list, left, right) {
if (left > right) {
return;
}
let i = left;
let j = right;
let pivot = list[left];
while (i != j) {
while ((list[j] >= pivot) && i < j) {
j--
}
while ((list[i] <= pivot) && i < j) {
i++
}
if (i < j) {
let tmp = list[i];
list[i] = list[j];
list[j] = tmp;
}
}
let meet = i;
let tmp = list[meet];
list[meet] = pivot;
list[left] = tmp;
partial(list, left, meet - 1);
partial(list, meet + 1, right)
}

console.log(qsort([32,45,37,16,2,87]))

JavaScript 归并排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function mergeSort(arr){
if(arr.length < 2){
return arr;
}
var middle = parseInt(arr.length/2);
var left = arr.slice(0,middle);
var right = arr.slice(middle);

return merge(mergeSort(left),mergeSort(right));
}

function merge(left,right){
var result = [];
var i = 0, j = 0;
while(i < left.length && j < right.length){
if(left[i] > right[j]){
result.push(right[j++]);
}
else{
result.push(left[i++]);
}
}
while(i < left.length){
result.push(left[i++]);
}
while(j < right.length){
result.push(right[j++]);
}

return result;
}

JavaScript 二分查找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function bin (list, target) {
if (list.length <= 1) {
return list[0] === target? 0: false;
}
let left = 0;
let right = list.length - 1;
while (left < right) {
let mid = Math.floor((left + right) / 2);
let center = list[mid];
if (center === target) {
return mid;
}
else if (center > target) {
right = mid
}
else {
left = mid + 1
}
}
return list[left] === target? left: false;
}

bin([11,12,13,14,15,16,17,18,19,20], 18) // 7

JavaScript [递归]版本斐波那契

1
2
3
4
5
6
7
8
9
function fib (x) {
if (x == 1) {
return 1;
}
if (x == 2) {
return 2;
}
return fib(x - 2) + fib(x - 1)
}

JavaScript [循环+有缓存]版本斐波那契

1
2
3
4
5
6
7
8
9
10
11
function fib (x) {
let cache = {
'1': 1,
'2': 2
};

for (let i = 3; i <= x; i++) {
cache['' + i] = cache['' + (i - 1)] + cache['' + (i - 2)]
}
return cache['' + x]
}

JavaScript [柯里化]版本斐波那契

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function kvcf() {
let cache = {
'1': 1,
'2': 2
};

let fib = function (x) {
if (cache['' + x] !== undefined) { return cache['' + x] }
return cache['' + x] = fib(x - 2) + fib(x - 1);;
}
return fib;
}

console.log(kvcf()(40)); // 165580141

0 - 3,999,999 长度为 4,000,000 的数组中出现过多少个字符 ‘1’ ?

1
2
3
4
5
6
7
8
求解:
1000000 / 10 = 100000
100000 * 6 = 600000
600000 * 4 = 2400000
2400000 + 1000000 = 3400000

答案:
3400000

框架

Vue

React

Redux reducer 必须是纯函数

1
2
3
reducer 必须是纯函数 必须返回一个新state 不能直接修改老state 

本质原因: Redux 判断更新是直接浅层比较 按引用传递会被误认为没有更新

vue 对比 Angular、 React

1
2
3
4
5
6
1. vue的设计倾向于一个Library | 而Angular倾向于一个Framework
2. Vue 3.0 放弃 Object.defineProperty 改用 ES6 Proxy (vue 1.0 - 2.0 的双向绑定定是基于ES5中的getter/setter) | 而Angular基于脏值检测 (从树根部深度遍历效率不高) | React使用setState手动通知数据模型变化
3. Angular使用TypeScript来开发
4. vue更拥抱基于CSS、JavaScript的经典的web开发 学习曲线更友好
5. React会在组件改变时大颗粒度地以其为根组件渲染其所有子组件 | 而vue会跟踪组件关系细粒度地渲染子组件变化
6. React使用JSX以及CSS-in-JS | 而vue即使在单组件内也保留了经典经典的HTML模板以及<style>标签编写CSS

vue生命周期

1
2
3
beforeCreate => created => beforeMount => mounted => beforeDestroy => destroyed
v ^
beforeUpdate -> updated

vue生命周期 - 数据与Dom节点驱动关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//  <div id="app">
// <h1>{{message}}</h1>
// </div>


------beforeCreate------
el : undefined
data : undefined
message: undefined

------created------ // 此时 data已初始化 dom节点还未创建
el : undefined
data : {"message":"你好 Vue"}
message: 你好 Vue






------beforeMount------ // 此时 dom节点已创建已添加到页面 {{message}}插值运算还未计算
el : <h1>{{message}}</h1>
data : {"message":"你好 Vue"}
message: 你好 Vue

------mounted------ // {{message}}插值运算结束
el : <h1>你好 Vue</h1>
data : {"message":"你好 Vue"}
message: 你好 Vue






>>> vm.message = '你好世界'



------beforeUpdate------ // data已经改变 virtualdom还未驱动dom更新
el : <h1>你好 Vue</h1>
data : {"message":"你好世界"}
message: 你好世界

------updated------ // 已更新dom
el : <h1>你好世界</h1>
data : {"message":"你好世界"}
message: 你好世界

vue observer/dep/watcher 以及 nextTick

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Observer: 数据的观察者,让数据对象的读写操作都处于自己的监管之下

Watcher: 数据的订阅者,数据的变化会通知到Watcher,然后由Watcher进行相应的操作,例如更新视图

Dep: Observer与Watcher的纽带,当数据变化时,会被Observer观察到,然后由Dep通知到 Watcher 更新而执行 queuewatcher()


// 示意:

$vm.data => Observer // 一个 data 对应[一个] Observer
v
Observer.walk // 通过 Observer.walk 多次 defineReactive()
v
for (..) {
const dep = new Dep().. // 实例化[多个] Dep
proxy(data) // Vue 3.0 使用 Proxy 代理多个 key 的 set 和 get
dep.depend().. addDep.. addSub.. // 添加[多个]实例化的 watcher 到数组 dep.subs
}


----> 拦截到 set ---> Observer 调用 dep.notify()
v
遍历subs去更新
V
for (...) {
queueWatcher() -> -> -> nextTick(flushSchedulerQueue)
}



// nextTick 原理

- queuewatcher 更新本身就用的 nextTick(flushSchedulerQueue)
- Vue.nextTick 依然使用 nextTick 即可保证回调进入事件队列队尾
- nextTick 基于 microTimerFunc (promise) 或者 macroTimerFunc (setImmediate, MessageChannel, setTimeout) 来实现

Angular在什么时刻更新视图?

1
2
3
4
5
6
7
8
9
// 主要在执行异步事件时
用户输入操作,比如点击,提交等
请求服务端数据
定时事件,比如setTimeout,setInterval

// 如何监听全部异步事件的?
Angular接入了ZoneJS 由它监听了Angular所有的异步事件
// ZoneJS是怎么做到的呢?
其实它重写了所有的异步api(所谓的猴子补丁Monkey patch)!ZoneJS会通知Angular可能有数据发生变化,需要检测更新。

vue key

1
2
3
4
5
6
7
v-for => 使用就地复用原则
// 数据顺序改变时不会移动 dom
// 而是就地改变元素
// 这个策略不适合类似表单输入框的场景

可以用 :key 来追踪元素
// 类似 vue 1.x 的 track-by

vue 异步组件原理

1
Vue 异步组件只在实际需要渲染组件时 才触发调用工厂函数

Cordova / RN 如何通信

1
2
3
4
5
6
7
8
9
// Cordova
// 通过打开gap://协议请求被native端拦截

... return prompt(argsJson, 'gap:'+JSON.stringify([bridgeSecret, service, action, callbackId]));

... window.webkit.messageHandlers.cordova.postMessage(command);

// native 把返回值和 callbackID 返回给 js
// 完成通信
1
2
3
4
5
// RN

React Native会在一开始生成OC模块表
然后把这个模块表传入JS中
JS参照模块表 就能通过JavaScriptCore执行而间接调用OC的代码(微信小程序方式类似)

Tips

weinre远端调试

1
2
安装
npm install -g weinre
1
2
启动
weinre --httpPort 8080 --boundHost -all-

safari无痕模式

1
2
safari无痕浏览模式
window.localStorage.setItem is true but call error

强类型 弱类型 静态类型 动态类型

1
2
3
4
强类型: 偏向于[不容忍][隐式类型转换]      
弱类型: 偏向于[容忍][隐式类型转换]
静态类型: ==[静态类型检查] [编译时]拒绝非法行为
动态类型: ==[动态类型检查] [运行时]拒绝非法行为

设计模式

1
2
3
4
5
6
1.单体/单例模式: 一个类只能保证有一个实例 例如对象字面量的方式创建一个单例

//先判断实例是否存在,存在则返回,不存在则创建,这样可以保证一个类只有一个实例对象
var test_simple = test_simple || {
name: 'alice'
}
1
2
3
4
5
2.1.观察者模式
观察者模式: 要求希望接收到主题通知的观察者必须订阅内容改变的事件

2.2.发布订阅模式
发布订阅模式: 使用了一个主题/事件通道 这个通道介于订阅者和发布者之间 其目的是避免订阅者和发布者产生依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
3.工厂模式
工厂模式: 提供创建对象的接口 封装一些公用的方法 如果实现具体的业务逻辑 可以放在子类重写父类的方法
优点: 弱化对象间的耦合 防止代码重复
缺点: 简单业务可以用 复杂的业务会导致代码维护性差 不易阅读

FruitCake.prototype.makeCake = function(type){
var cake;
switch (type){
case 'apple':
cake = new AppleCake();break;
case 'pear':
cake = new Pear();break;
default:
cake = new Orange();break;
}
return cake;
}

ES Include Has In

1
2
3
'2' in {'2': 2}; // true
[1,2].includes(2); // true
(new Set([1,2])).has(2); // true

(hybrid)app项目-上线流程

1
开发=>开发打包=>开发自测=>测试环境打包=>测试(测试同学直接去下载测试包)=>正式发版本=>更新到渠道

nvm

1
2
nvm管理node版本   
nvm use -v 0.10.32

package.json版本固化

1.2.2 => 大版本号.次要版本号.小版本号

1
2
3
波浪号(tilde)+指定版本:安装时不改变大版本号和次要版本号。
插入号(caret)+指定版本:安装时不改变大版本号。
latest:安装最新版本。

console.log的lazy

1
控制台打印的[折叠的][按引用传]的对象,把折叠打开后才拿到折叠起来的值。

hybrid-本地xhr

1
可以在服务端配跨域access-allow-origin为'null'

CORS 如何带 Cookie

1
2
3
xhr.withCredentials
+
Access-Control-Allow-Credentials: true

hybird-base64

1
base64图片为纯本地图片-不需要发网路请求

HTML5 废弃的标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1.可以使用css代替的标签
删除 basefont,big,center,font,s,strike,tt,u 这些纯表现的元素 html5中提倡把画面展示性功能放在css中统一编辑

2.html5不再使用frame
不再用 frame,noframes和frameset,这些标签对可用性产生负面影响。HTML5中不支持frame框架,只支持iframe框架,或者用服务器方创建的由多个页面组成的符合页面的形式,删除以上这三个标签。

3.只有个别浏览器支持的标签
bgsound 背景音乐,blink 文字闪烁,marquee 文字滚动, applet 用于插入JAVA渲染的代码

4.其他不常用的标签
ul替代dir
pre替代listing
code替代xmp
ruby替代rb
abbr替代acronym
废除isindex使用form与input相结合的方式替代。
废除listing使用pre替代
废除nextid使用guids
废除plaintex使用“text/plian”(无格式正文)MIME类型替代。

CSS3 硬件加速

1
2
3
4
transform => 不会触发 repaint
// 一点非常类似3D绘图功能
// 最终这些使用 transform 的图层都会由独立的合成器进程进行处理
// opacity filter 也支持硬件加速

CSS 实现 Retina 1px

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1.border-image 

2.linear-gradient

.border {
background:
linear-gradient(180deg, black, black 50%, transparent 50%) top left / 100% 1px no-repeat,
linear-gradient(90deg, black, black 50%, transparent 50%) top right / 1px 100% no-repeat,
linear-gradient(0, black, black 50%, transparent 50%) bottom right / 100% 1px no-repeat,
linear-gradient(-90deg, black, black 50%, transparent 50%) bottom left / 1px 100% no-repeat;
}


3.box-shadow

.hairlines li {
border: none;
box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.5);
}


4.淘宝使用 initial-scale 0.5/0.333

<meta name="viewport" content="initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">
<meta name="viewport" content="initial-scale=0.3333333333333333, maximum-scale=0.33333, user-scalable=no">


5.伪元素 1px scale(0.5)

node 如何读取一个 2G 的文件

1
一次读取一行 边读边处理边写

Git 冲突

1
2
3
4
5
6
7
8
9
10
11
<<<<<<< HEAD
msg: db "HELLO ASM", 10
=======
msg: db "Hello man", 10
>>>>>>> origin/as

git会对[不同行修改]自动merge`
git对[同行修改]会冲突`
留下=======作为冲突代码块的区分`
解决冲突:
手动修改conflict的代码`->`git add`->`git commit`->`git push`

Git 切换到远程分支操作

1
git checkout -b apple origin/apple

Git 工作区 暂存区

1
2
3
4
5
6
7
8
9
10
11
12
13
工作区 (Working Directory):
就是你在电脑里能看到的目录

暂存区 (stage):
工作区有一个隐藏目录 .git 这个不算工作区 而是Git的版本库。
Git的版本库里存了很多东西 其中最重要的就是称为stage(或者叫index)的暂存区

添加+提交 (add+commit)
git add: 把文件添加进去 实际上就是把文件修改添加到暂存区
git commit: 提交更改 实际上就是把暂存区的所有内容提交到当前分支。
一旦提交后 如果你又没有对工作区做任何修改 那么工作区就是"干净"的

(HEAD就是当前活跃分支的游标)

Webpack Cool Plugin

1
2
1.Tree Shaking: 剔除无用 JS 死代码
2.Webpack Bundle Analyzer: 打包分析

node调试

1
移动端调试:直接localhost换成真实ip访问

Hello World

Posted on 2017-01-24 | Edited on 2019-02-14

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

12

卡牌大叔

13 posts
6 tags
RSS
GitHub Weibo Twitter
© 2019 卡牌大叔
Powered by Hexo v3.8.0
|
Theme – NexT.Mist v7.0.0