小程序1rpx边框在部分ios设备下显示不全

原因:当标签的父容器宽度(单位rpx)÷2的值为偶数或偶数.5的时候会出现该bug,那么我们可以推到出用200.5*2=401,302*2=604等等都会重现这个bug
解决方案:
第一种方式是设置标签父容器的宽度到无bug值,即(奇数或奇数.5)*2,例如281*2rpx,281.5*2rpx可以解决;(有时候这个也不一定解决)
第二种方式是补充像素单位:

wxml中为有问题的标签旁边增加space的view后如下:

<view class='label-con'>
    <view class='space'></view>
    <view class='label'>a</view>
    <view class='label'>b</view>
    <view class='label'>c</view>
    <view class='label'>d</view>
</view>

wxss中增加space类为:

    .space{
        width: 1rpx;
        height: 100%;
        float: left;
    }

使用纯css制作一个简单的幻灯片效果

思路:使用css的帧动画来实现

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>纯css幻灯片</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
        * {
            margin: auto;
            padding: 0;
        }

        div {
            margin-top: 100px;
            width: 100px;
            border: solid 1px #ccc;
            overflow: hidden;
        }

        ul {
            float: left;
            display: flex;
        }

        ul li {
            width: 100px;
            height: 100px;
            text-align: center;
            line-height: 100px;
            list-style: none;
            animation: slide 5s ease 0s infinite normal both;
        }

        /*停顿(开始)>左移>停顿>左移>归零(结束)  此为三张图片  多一张图片加一个停顿+左移*/
        @keyframes slide {
            0% {
                transform: translateX(0%);
            }

            16.7% {
                transform: translateX(0%);
            }

            33.3% {
                transform: translateX(-100%);
            }

            50% {
                transform: translateX(-100%);
            }

            66.7% {
                transform: translateX(-200%);
            }

            83.3% {
                transform: translateX(-200%);
            }

            100% {
                transform: translateX(0%);
            }
        }
    </style>
</head>

<body>
    <div>
        <ul>
            <li>1</li>
            <li>2</li>
            <li>3</li>
        </ul>
    </div>
</body>

</html>

当你和老板或者同事发生意见分歧的时候,如何处理?

这个问题出自世界最常见的50道面试题(the 50 Most Common Interview Questions)
这个问题要测试你的有两个方面:你在职场上的政治常识和你是否敢说话。

职业生涯策略专家Mary Grace Gardner说:如果你的老板犯了一个错误,或者你有一个更好的点子,他一定是希望你能说出来的,但要注意你的表达方式,这非常重要。

职业生涯教练Angela Copeland说:这个问题最好的回答方式就是用一个例子来说明这个分歧。说服你的老板按照你的方式做一次尝试,最终让所有人都获得好处,包括你的领导。

例如:在一家的公司里面,你觉得远程办公对你的公司的企业文化有提升,但是老板开始可是并不赞成的。你就努力的去说服他一定要做一次尝试。结果效果非常好。

这个问题考验的不是让你如何说出老板的想法很差,而且你处理分歧的方式。

网页标准和标准制定机构重要性

其实任何行业都应该由一个机构来制定行业标准,例如汽车行业,手机行业等…
比如汽车有各种车型,长宽高都在一个标准的范围内,这样停车位就比较好设计,还有轮胎标准,座椅标准等等。
手机的屏幕分辨率,按键,系统都有一定的标准。如果手机尺寸固定为3种的话,是不是手机壳和手机膜的厂商最高兴了,这样就更容易设计产品,也节省了成本。

网页标准是由w3c组织制定的,最主要的工作就是统一浏览器规范,最小各个浏览器之间的差距。
在浏览器兼容性最差的年代 ie6和firefox和chrome的差别真的是让我们这些早期的前端工程师伤透了脑筋。80%的工作时间都浪费在调试兼容性上面了。
所以标准制定的越好,对于开发人员来说受益是最大的,开发时间变快了,bug变少了。对于使用者来说用户体验也更一致了。

渐进式增强和优雅降级区别

这两个概念的结果是一样的,区别就是一开始你从完整版入手还是基础版入手。
举个例子,玩pc游戏的时候经常会在游戏图像设置里面选择特效级别,高中低。
渐进增强(progressive enhancement)- 就是开发页面的时候先从一个低特效开始,然后根据更高版本的浏览器逐渐添加更高级别的效果。
优雅降级(graceful degradation)- 先从最高的特效开始开发,然后针对低版本浏览器做优化
应用场景:
例如ie6不支持阴影,火狐支持。
渐进增强就是先不保证ie6正常显示,然后再判断,如果当前浏览器支持阴影则显示。
优雅降级反之。

手写事件管理类(EventBus)-简易的观察者模式

因为小程序目前的基础库版本不支持全局的事件订阅和触发,只能自己写一个了,不然小程序里面没有vuex之类的东西用还真是不方便。

/* 简易的观察者实现
 * @Author: Liuxiaofan 
 * @Date: 2018-10-25 16:24:51 
 * @Last Modified by: Liuxiaofan
 * @Last Modified time: 2018-10-25 17:24:17
 */
class EventBus {
    constructor() {
        this.events = {}
    }
    getEvents() {
        return this.events || (this.events = {});
    }
    getListeners(eventName) {
        let events = this.getEvents();
        return events[eventName] || (events[eventName] = []);
    }
    on(eventName, fn, time) {
        time = typeof (time) == 'number' ? time : -1;
        time = time >= -1 ? time : -1;
        var listeners = this.getListeners(eventName);
        var listenerWrapper = {
            listener: fn,
            time: time,
        };
        listeners.push(listenerWrapper);
        return this;
    }
    off(eventName) {
        var events = this.getEvents();
        events[eventName] = [];
    }
    removeListener(eventName, listener) {
        var listeners = this.getListeners(eventName);
        for (var i = 0; i < listeners.length; i++) {
            if (listeners[i].listener == listener) {
                delete listeners[i];
            }
        }
    }
    trigger(eventName, args) {
        var listeners = this.getListeners(eventName);
        for (var i = 0; i < listeners.length; i++) {
            var listener = listeners[i];
            if (listener.time != -1) {
                listener.time--;
            }
            if (listener.time == 0) {
                this.removeListener(eventName, listener.listener);
            }

            listener.listener.apply(this, args || []);
        }
    }
    emit(eventName) {
        var args = Array.prototype.slice.call(arguments, 1);
        return this.trigger(eventName, args);
    }
}
export default EventBus

vuex 官方教程平民翻译版 – 核心概念

如果你看不懂vuex的官方教程,那就对了,因为他们写的不够平民化,而且用了很多术语来迷惑对手,来看看我翻译的平民版教程吧。

问:VUEX有什么用?
答:管理全局变量和相关的全局方法。当你需要子子通信的时候就知道了,如果一层一层的传值一定会把你烦死,所以你需要一个公用的全局对象去管理一些全局的变量和方法。
vuex就是eventbus(vue2.0之后改名叫store模式了)的升级版。jquery时代和backbone时代可以使用观察者模式实现,也有人使用nodejs里面提取的EventEmitter.js来实现“状态管理”,不过那时候还没有“状态管理”这么高大尚的词,我们都叫事件管理或者事件模型。

store 模式

这是一个精简版的vuex

// 定义全局对象 store
var store = {
    debug: true,
    state: {
        message: 'Hello!'
    },
    setMessageAction (newValue) { 
        if(this.debug) console.log(’setMess',newValue)
        this.state.message = newValue
    },
    clearMessageAction () {
         if(this.debug) console.log(’clearMess')
        this.state.message = ''
    }
}
// 组件里面声明好那些属性是私有的哪些是公共的
var vmA = new Vue({
  data: {
    privateState: {},
    sharedState: store.state
  }
})

var vmB = new Vue({
  data: {
    privateState: {},
    sharedState: store.state
  }
})

store内所有state值变化的操作方法都要放到store对象自身的方法里面去实现。然后再用一个log来记录每次操作。这样控制台里面就能看到所有与state变化的相关操作记录了。

基于上面的简单store模式,继续延伸约定。你不应该在各自的子组件里面直接修改store内的state里面的值。应该定义一个全局的公共对象来执行action来分发事件通知store去改变才行,这才是flux架构。
flux架构的好处是不仅能够管理store中state改变的记录。还能记录变更、保存状态快照、历史回滚/时间旅行。如果不理解这些概念就往下面看看vuex的思想吧

1. State

单一状态树 —— 只用1个对象来保存所有数据源,这样每个应用就只可以有1个store对象。相当于以前的全局对象。

1.1 在vue组件中获得vuex状态
其实跟读取data中的数据一样,使用computed方法,把store里面的属性返回出来

const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return store.state.count
    }
  }
}

1.2 mapState 辅助函数
当需要读取store对象内多个属性的时候,如果写多个computed方法是不是太麻烦,而且代码冗余,mapState就是为了解决这个问题

computed: mapState({
    // 箭头函数可使代码更简练
    count: state => state.count,

    // 传字符串参数 'count' 等同于 `state => state.count`
    countAlias: 'count',

    // 为了能够使用 `this` 获取局部状态,必须使用常规函数
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })

// 计算属性的名称与 state 的子节点名称相同时
computed: mapState([
  // 映射 this.count 为 store.state.count
  'count'
])

// 对象展开运算符
computed: {
  localComputed () { /* ... */ },
  // 使用对象展开运算符将此对象混入到外部对象中
  ...mapState({
    // ...
  })
}

2. Getter

store中的state只是保存固定的单一数据就相当于vue组件里的data属性一样。那么getter就相当于vue组件里的computed。
下面代码中的state换成data, getters换成computed是不是就理解了

const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    }
  }
})

2.1 调用方式

// 通过属性访问
store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]
// 通过方法访问
// 通过让 getter 返回一个函数,来实现给 getter 传参
getters: {
  // ...
  getTodoById: (state) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
}
store.getters.getTodoById(2)

2.2 mapGetters 辅助函数
跟mapState类似

 computed: {
  // 使用对象展开运算符将 getter 混入 computed 对象中
    ...mapGetters([
      'doneTodosCount',
      'anotherGetter',
      // ...
    ])
  }

3. Mutation

3.1 mutation更改store中state属性值的唯一方法,把mutation换成setStates是不是就好理解了

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 变更状态
      state.count++
    }
  }
})

但是mutation毕竟不是setState,根据约定,你不可以直接调用mutation内声明的方法,因为mutation只负责注册事件。那么如何触发它定义的事件呢?这样:

store.commit('increment’) //把commit换成trigger是不是更好理解

3.2 提交载荷(Payload)
触发事件方法的参数你就说参数,非要起个难理解的名词来显示自己很高大上?

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
// 注意payload参数要用对象的形式,因为人家这么约定的
store.commit('increment', {
  amount: 10
})
// 另外一种写法
store.commit({
  type: 'increment',
  amount: 10
})

3.3 Mutation 必须是同步函数,里面不要出现异步操作,因为异步操作你就不知道每个方法执行的顺序了。

3.4 在子组件中提交Mutation
你可以直接在方法里面使用this.$store.commit(‘xxx’)或者使用mapMutations辅助函数,跟上面那两个辅助函数类似,这样写:

import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    ...mapMutations([
      'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`

      // `mapMutations` 也支持载荷:
      'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
    ]),
    ...mapMutations({
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
    })
  }
}

4. Action

上面说到Mutation必须要使用同步函数,那么Action就是来解决你想使用异步函数的这个事情。先看一下Action 怎么写:

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) { // 注意context这个参数有点不一样
      context.commit('increment')
    }
  }
})

乍一看context是不是就等于store,最后介绍Moudles的时候你就知道区别了。实践中一般都是使用ES2015的参数解构方式简化代码:

actions: {
  increment ({ commit }) {
    commit('increment')
  }
}

4.1 分发Action

store.dispatch('increment')

乍一看是不是跟mutations没什么区别,实际上真就没什么区别,atciont其实就是专门为了异步操作来设计的。如果你有异步的方法就写在action里面,如果是同步的就写在mutations里面,就这么简单,别想复杂了。

actions: {
  incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}

官网那个购物车示例如果看不懂,看一下这个平民版:

actions: {
  checkout ({ commit, state }, products) {
    const id = state.id
    const savedCartItems = state.cart.items
    $ajax.postProduct({
        data:{
            id,
            products
        }
     }).then(res =>{
        commit(types.CHECKOUT_REQUEST)
     }).catch(res =>{
        commit(types.CHECKOUT_FAILURE, savedCartItems)
     })
  }
}

5. Module

我们知道一个应用只允许使用一个store,这样做有个问题就是,当业务越来越复杂的时候,这个对象里面就会有N多的属性和方法,所以我们要把store拆成一个个的小份,然后根据业务去分类,以便维护。
Module其实就是为了让你的代码看起来更整洁的,如果业务没那么复杂不用也可以。

const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

5.1 模块的局部状态
注意参数mutation和getter方法里面的第一个参数是模块的局部状体对象

const moduleA = {
  state: { count: 0 },
  mutations: {
    increment (state) {
      // 这里的 `state` 对象是模块的局部状态
      state.count++
    }
  },

  getters: {
    doubleCount (state) {
      return state.count * 2
    }
  }
}

根节点状态用context.rootState表示:

const moduleA = {
  // …
 getters: {
    sumWithRootCount (state, getters, rootState) {
      return state.count + rootState.count
    }
  },
  actions: {
    incrementIfOddOnRootSum ({ state, commit, rootState }) {
      if ((state.count + rootState.count) % 2 === 1) {
        commit('increment')
      }
    }
  }
}

JS进行base64编码

现在后端API流行使用HTTP Basic Authentication来发送用户名和密码,但是要求必须把用户名和密码进行base64编码才行。
postman等工具可以点一下按钮就帮你编好。PHP提供了base64_encode()方法。JS以前通过专门的base64.js插件来实现。
不过ie10之后的版本源生JS已经支持base64编码,方法就是window.btoa 和 window.atob。
如果你的项目是移动端项目就可以大胆的使用btoa和atob这两个方法了,没有兼容性问题。
可以像这样简单的封装一个方法:

 function makeBaseStr (user, password) {
      // atob() //base64解密方法
      var token = user + ':' + password
      var hash = btoa(token)
      return 'Basic ' + hash
    }