特色

2018年,前端应该怎么学?

面向2018年,我觉得前端有这么三个方向可以突破

  1. 前端逻辑层(包括三大框架,webpack,前端数据管理)
  2. 前端交互层(包括css3,canvas,svg,vr等,以及对应性能优化)
  3. node服务器层 (即服务器领域,以及前端开发工具的开发)

当然还可以说,有electron这样开发桌面应用的,但是我觉得大部分人的规划还是在web领域范畴的。

一、前端逻辑层

前端逻辑层解决的问题:前端的渲染问题。
其实就是,从后端拿到的各种数据(数据库的结构化数据,各种素材),如何组合成页面元素。

前端的渲染,其实就是 模板+数据 = 页面

① html 负责结构
② css 负责样式
③ js 负责逻辑
只有模板没有后端数据的页面我们可以理解为静态页面
而我们目前主流的三大框架,则提供 模板与数据之间的映射关系。
在逻辑层的研究,主要就是学习并熟练三大框架: Vue, React, Angular

这一块是我们90%前端,日常工作的主要部分。也是很多公司面试会去重点考察的内容。
但是,目前的行情已经不是,你会个新框架就可以任性拿OFFER的时期了。
前端的井喷的情况下,仅仅会一些基础知识是不够的。你必须往下深挖。

可以深挖的部分
框架部分: 举个例子,如 Vue+ Vuex+ Vue-router+axios的全家桶
react和angular的也类似,有这么一套全家桶。
要想在前端竞争中取得优势,需要在这些框架的原理上深挖。
研究: a. 源码和实现原理 (理论)

     b. 常见的应用场景和常见问题的实现 (经验)
     c. 同类问题的横向对比(比如用了vuex 需要了解一下 redux是怎么实现的)

构建部分: webpack + npm/yarn + 脚手架(如vue-cli) +sass
构建部分,其实是一块很容易出区分度的领域。你研究透一点,你就越能驾驭住项目的变化。
研究: a. webpack的配置和对应应用场景

    b. npm的命令使用,以及一些高级的用法(架设私有npm或者自己写全局包等)
    c. 脚手架快速搭建的过程,以及如何自定义更改
    d. 如何配置eslint和单元测试等

代码规范部分: es6 + ts + css规范 等等等
代码规范不多说,前端的js和css都是设计得很粗糙的语言。如果你想hold住更大型的项目,那么规范是你不可忽视的地方。

逻辑层部分,是我们通常意义上理解的前端本职。

二、前端交互层

所谓交互层,可以理解为我们常说的 特效。
当前的趋势下,移动端浏览器的性能逐步提升。未来一个大的趋势就是
前端开发的效果,正无限逼近原生效果

同时一些大厂也在纷纷提前布局了,之前惊艳四方的天猫造物节 H5
淘宝造物节-风靡朋友圈的h5怎么做的?

如果我们要在这波浪潮上取得先机,那么就必须提前学习。
也许再过三年,前端的开发特效的能力,也会成为标配技能。

可以分三块来看
1.呈现效果学习:
很多同学不是不会特效,而是不知道要实现成什么样子。用前端的各种东西鼓捣了,终于出来了特效,但是效果却很难看。
不不不,做特效,应该先把特效样子调好了再去做。
建议学习Adobe Effects (AE),直观地看动效是什么样子,然后再翻译成前端的动效。

2.相关数学/视觉原理:
a.贝塞尔曲线原理
b.视差原理
c.阻尼公式

3.相关技术手段实现
前端目前实现动画特效的八种方式
http://www.offcn.com/it/2017/…

①gif: 设计直接出GIF,前端直接用
②逐帧动画: css3 step属性 或 js制作逐帧动画
③css3 : transition/ transform/animation
④svg : svg 很适合移动端
⑤canvas: canvas适合一些比较大面积的动效
⑥flash 转 canvas : 这个我不了解
⑦video: 用h5 video标签
⑧js动画: 可以配合平台提供的api(重力感应等)开发更复杂的动效

三、node 服务层

node在前端应该是无人不知了。在实际使用中node的使用场景应该有这几个:
① 作为前端构建辅助工具: 如各种脚手架中,经常有node的参与
② 作为服务端做渲染层: 实现接口合并和模板渲染
③ 作为完整服务器: 一般是创业公司中,可以一人搭建起全部web功能。如微信公众号项目等

那么如果要学习的话,要从哪里入手呢?
学习一个新技术,我个人的方式都是
先把原生的文档,快速过一遍,然后学框架。

不看原生的,很容易学一套框架,就是一次学习量。
学习原生知识,可以让你学框架速度提升。

框架里面常用的:
①express ②koa ③基于以上两者衍生的框架 ④其余框架

我建议新手学习express 框架。理由很简单,易学,使用多,教程多。
我个人想学习的node框架是天猫的egg.js 。

推荐理由是
①大厂开发,面向工程
②经受住了双十一的考验,质量有保障
总结

前端是端工程师,一切特性依赖于客户端。
因此各位前端的同仁,除了上述的功能外,还是要多留意浏览器以及移动端环境的变迁。
一些类似与 HTTP2协议,PWA,openGL等技术,都随时有可能改变前端的工作方式。

我上面列了那么多,其实我很多都没有学过(手动狗头)。但是除了技术能力,也许以下几个综合能力,对于前端er也是要注意的
①自我规划能力:对于技术领域的趋势的洞察,以及个人对应的学习计划
②快速学习能力:搜索能力(利用网上的各种知识),知识迁移能力
③沟通能力和知识输出能力: 面向产品,以及面向后端,面向社区同仁

特色

前端知识点总结——VUE

1.框架和库的区别:

框架:framework 有着自己的语法特点、都有对应的各个模块
库 library 专注于一点

框架的好处:

1.提到代码的质量,开发速度
2.提高代码的复用率
3.降低模块之间的耦合度
(高内聚低耦合)

UI:user interface
GUI : graphical user interface
CLI : command line interface
API : application interface

思维模式的转换:

从操作DOM的思维模式 切换到 以数据为主

2.Vue概述

1、what    
    是一个渐进式的构建用户界面的js框架
2where
    小到的简单的表单处理,大到复杂的数据操作比较频繁的单页面应用程序
3、why
    1.方便阅读的中文文档
    2.容易上手 (学习曲线比较缓和)
    3.体积小
    4.基于组件化的开发方式
    5.代码的可读性、可维护性得到了提高
4、how
    工作方式:可以通过丰富的指令扩展模板,可以通过各种各样的插件来增强功能

    搭建环境:
    方式1
         全局安装 vue-cli
        $ npm install --global vue-cli
        # 创建一个基于 webpack 模板的新项目
        $ vue init webpack my-project
        # 安装依赖,走你
        $ cd my-project
        $ npm install
        $ npm run dev
    方式2:
        直接引入对应的js文件

3.Vue中基础知识

1、双花括号
    mustache(胡子)/interpolation(插值表达式)

    语法:
    <any>{{表达式}}</any>
    作用:
    将表达式执行的结果 输出当调用元素的innerHTML中;还可以将数据绑定到视图

    

2、指令-循环指令
    基本语法1:
    <any v-for="tmp in array"></any>
    基本语法2:
    <any v-for="(value,index) in array"></any>
    
    作用:
    在遍历array这个集合时,将临时变量保存在tmp中,创建多个any标签

3、指令-选择指令
    语法:
      <any v-if="表达式"></any>
      <any v-else-if="表达式"></any>
      <any v-else="表达式"></any>
    作用:
      根据表达式执行结果的真假,来决定是否要将当前的这个元素 挂载到DOM树


4、指令-事件绑定
    语法:
        <any v-on:eventName="handleEvent"></any>
    作用:
        给指定的元素 将handleEvent的方法绑定给指定eventName事件
    

5、指令-属性绑定
  基本语法:
    <any v-bind:myProp="表达式"></any>
    补充,支持简写:
    <any :myProp="表达式"></any>
  作用:
    将表达式执行的结果 绑定 到当前元素的myProp属性

    <img v-bind:src="'img/'+myImg" alt="">
   动态样式绑定
    <p :style="{backgroundColor:myBGColor}">动态样式绑定</p>

   动态样式类绑定
      <h1 :class="{myRed:false}">动态样式类的绑定</h1>

6、指令-双向数据绑定
    方向1:数据绑定到视图
    方向2:将视图中(表单元素)用户操作的结果绑定到数据

    基本语法:
      <表单元素 v-model="变量">
      </表单元素>


4.组件化

组件:组件就是可被反复使用的,带有特定功能的视图

所谓的组件化,就像玩积木一样,把封装的组件进行复用,把积木(组件)拼接在一起,构成一个复杂的页面应用程序。

组件树就是由各个组件构成的一种数据结构,它存在的意义是为了帮梳理应用程序

1、组件的创建
  全局组件
    Vue.component('my-com',{
      template:`
        <h2>it is a header</h2>
      `
    })
      局部组件
    new Vue({
        components:{
            'my-footer':{
           template:''
         }
        }
    })
2、组件使用
    作为普通的标签去使用
    <my-com></my-com>


3、注意事项
    1.组件的id和使用方式 遵循烤串式命名方式:a-b-c 
    2.如果一个组件 要渲染多个元素,将多个元素放在一个顶层标签中,比如div、form
    3.全局组件可以用在id为example的范围内的任何一个组件内部,直接调用可以;但是局部组件只能在父模板中直接调用


5.自定义指令

1、创建和使用
 Vue.directive('change',{
    bind:function(el,bindings){
    //首次调用
    },
    update:function(el,bindings){
    //只要是有数据变化,都会调用
    },
    unbind:function(){
    //解绑
    }
 })
 <any v-change="count"></any>


6.过滤器

过滤器是针对一些数据 进行筛选、过滤、格式化等相关的处理,变成我们想要的数据

过滤器的本质 就是一个带有参数带有返回值的方法

Vue1. 支持内置的过滤器,但是Vue2. 就不再内置过滤器,但是支持自定义过滤器。

1、过滤器的创建和使用

1.创建
   Vue.filter(
    'myFilter',
    function(myInput){
       //myInput是在调用过滤器时,管道前表达式执行的结果
       //针对myInput,按照业务需求做处理
       //返回
       return '处理后的结果'
    })

2.使用
    <any>{{expression | myFilter}}</any>

2、如何在调用过滤器时,完成参数的发送和接受

1.发送
<any>{{expression | myFilter(参数1,参数2)}}</any>

2.接受
Vue.filter('myFilter',function(myInput,参数1,参数2){
    return '处理后的结果'
})


7.复合组件

知识回顾:
  Vue.component('my-header',{
    template:`<div></div>`
  });

  <my-header></my-header>

 复合组件:并不是新的概念,就是一个组件,只不过这个组件中 可以调用其他的组件

 注意事项:
  1.组件要渲染的内容 取决于在定义组件时template
  
  <my-list>
    <my-item></my-item>
  </my-list>
  效果是出不来的
  2.允许在一个组件中,直接来调用另外一个组件


8.生命周期

四个阶段:
    create 准备工作 (数据的初始化。。。)
    mount  挂载前后针对元素进行操作
    update 数据发生变化,
    destroy 清理工作 (关闭定时器、集合清空..)

    beforeCreate/created
    beforeMount/mounted
    beforeUpdate/updated
    beforeDestroy/destroyed


9.常用属性

1、watch
       1. 表单元素的双向数据绑定
   v-model="myValue"
   2.监听
    watch:{
    myValue:function(newValue,oldValue){
    
    }
    }
2、computed
    计算属于是用于在模板中,搞定复杂的业务逻辑,因为有依赖缓存。
    1.指定计算属性
        computed:{
          myHandle:function(){
           return 数据
          }
        }

    2.调用
        <any>{{myHandle}}</any>


10.组件间通信

1、父与子通信 (props down)
    1.发送
        <son myName='zhangsan'>
        </son>
    2.接受
        到son组件:
        Vue.component('son',{
          props:['myName'],
          template:`
           <p>{{myName}}</p>
          `
        })
    
2、子与父通信 (events up)
     1.绑定
    methods:{
     handleEvent:function(msg){}
    }
    <son @customEvent="handleEvent"></son>
    2.触发
    子组件内部:
    this.$emit(‘customEvent’,100);

3、ref(reference 引用/参考 外号)
 帮助在父组件中 得到子组件中的数据、方法。
    1.指定ref属性
    <son ref="mySon"></son>

    2.根据ref得到子组件实例
    this.$refs.mySon

4、$parent
    this.$parent得到父组件的实例

5、兄弟组件通信
    1.var bus = new Vue();
    2.接收方
    bus.$on('eventName',function(msg){})
    3.发送方
    bus.$emit('eventName',123);

11.补充组件创建的方式

1、直接在template属性中指定模板内容
    1.全局组件
    Vue.component
    2.局部组件
    {
      components:{
        'my-footer':{template:``}
      }
    }
2、.vue结尾的文件
    <template></template>
    <script></script>
    <style></style>
3、单独指定一个模板内容
    <script
    id='myContent'
    type='text/x-template'>
    </script>

    Vue.component('',{
      template:'#myContent'
    })

12.路由模块

路由模块的本质 就是建立起url和页面之间的映射关系

1、SPA的基本概念和工作原理

SPA:single page application 单一页面应用程序,只有一个完整的页面;
它在加载页面时,不会加载整个页面,而是只更新某个指定的容器中内容。
比如Gmail、移动的webApp


工作原理:
1.解析地址栏 
    完整的页面地址、路由地址
2.根据路由地址 从路由词典中找到真正的要加载的页面
3.发起ajax请求 
    请求要加载的页面
4.像指定的容器中 插入加载来的页面

2、路由模块的基本使用

专业术语: 
    router路由器 
    route路由
    routes 路由数组(路由词典)
1.引入vue.js vue-router.js
2.指定一个容器
<router-view></router-view>
3.创建业务所需要用到的组件类
        var MyLogin = Vue.component()
4.配置路由词典
 const myRoutes = [
  {path:'',component:MyLogin},
  {path:'/login',component:MyLogin}
 ];

 const myRouter = new VueRouter({
  routes:myRoutes
 })

 new Vue({
   router:myRouter
 })
5.测试
    修改地址栏中的路由地址,测试看加载的组件是否正确

注意事项:
 1.先引入vue,再引入插件
 2.一定要指定router-view
 3.route路由 {path:'',component:}
  routes 路由数组 []
  router 路由器:按照指定的路由规则去访问对应的组件 new VueRouter



3、使用路由模块来实现页面跳转的方式

方式1:直接修改地址栏
方式2:js
this.$router.push(‘路由地址’);
方式3:
 <router-link 
 to="路由地址"></router-link>

4、完成参数的传递

在页面之间跳转的时候,在有些场景下,需要同时指定参数

1.明确发送方和接收方
list --20--> detail
1.配置接收方的路由地址
/detail --》 /detail/:index

this.$route.params.index
2.发送
routerLink to="/detail/20"
this.$router.push('/detail/20')


5、路由嵌套

在一个路由中,path对应一个component,如果这个component需要根据
不同的url再加载其他的component,称之为路由的嵌套

举例:比如A组件现在需要根据不同的url,加载B组件或者C组件
1.给A组件指定一个容器
  <router-view></router-view>
2.配置路由词典
  {
    path:'/a',
    component:A,
    children:[
      {path:'/b',component:B}
    ]
  }

  需求:现在有两个组件,分别是login/mail,建立SPA。
  在此基础上,希望mail组件 嵌套inbox/outbox/draft

  补充:在设置子路由,路由匹配规则依然是适用的,
  只不过路由地址为空和异常,要携带父组件的路由地址
  /mail /mail/draft

13.搭建基于CLI开发环境的方式

1.指定一个文件夹
C:\xampp\htdocs\framework\vue\project
2.将tpls.zip拷贝到project中 
3.右键单击压缩包,解压缩到当前文件夹
4.进入到tpls
5.同时按下shift和鼠标右键,选择在此位置打开命令行串口
6.执行npm install
7.执行npm start

14.axios

1.axios的get方法

export const getAjax= function (getUrl,getAjaxData) {
  return axios.get(getUrl, {
    params: {
      'getAjaxDataObj1': getAjaxData.obj1,//obj1为getAjaxData的一个属性
      'getAjaxDataObj2': getAjaxData.obj2
    }
  })
}

2.axios的post方法

export const postAjax= function (getUrl,postAjaxData) {
return axios.get(postUrl, {

  'postAjaxDataObj1': postAjaxData.obj1,//obj1为postAjaxData的一个属性
  'postAjaxDataObj2': postAjaxData.obj2

})
}

3.axios的拦截器
主要分为请求和响应两种拦截器,请求拦截一般就是配置对应的请求头信息(适用与常见请求方法,虽然ajax的get方法没有请求头,但是axios里面进行啦封装),响应一般就是对reponse进行拦截处理,如果返回结果为[]可以转化为0

1.请求拦截:将当前城市信息放入请求头中

axios.interceptors.request.use(config => {
config.headers.cityCode = window.sessionStorage.cityCode //jsCookie.get(‘cityCode’)
return config
},

2.响应拦截:处理reponse的结果

axios.interceptors.response.use((response) =>{
  let data = response.data
  if(response.request.responseType === 'arraybuffer'&&!data.length){
    reponse.date=0
  }
})
特色

前端工程师简历中的项目经验怎么写

有不少前端工程师,在写简历时就发愁。简历中的项目怎么写,怎么描述。觉得自己虽然工作了好几年,做过许多项目,但是觉得都没做什么高大上的事情,自然就觉得没啥可写的。或者觉得做的事情都一样,写来写去都那几样。

这里我献丑,贡献几个项目经验写法的小实例!


一、第一种项目风格

项目:腾讯管家前端动画

作品描述:该项目将一个完整flash剧情动画还原成一个由JS+CSS3实现的前端动画。

链接:https://dxb123456.github.io/tengxun/

实现技术:CSS+HTML+JS+H5+CSS3+jqury;

项目难点

1.定时器的清除
部分动画效果需要js自动生成,时间的控制使用了timeout和innertal,其中timerout包含了innertal,点击事件和定时器不在同一个js文件中,快速来回点击的时候,定时器清除不起作用。
解决方案:将该li对应页的所有定时器绑定在该li身上,每次点击的时候清除timeout和innertal。

2.带阴影折线运动处理
对于倾斜的p通过js改变其高度,并且按照数学逻辑改变top和left值的情况下,p在运动时候会出现偏移,和抖动。
解决方案:给p一个运动基准点,这样p在运动的时候就无需改变top和left值,只需要改变宽度或高度即可。

3.抛物线的运动
css中两个点运动都是直线运动。
解决方案:给初始点一个旋转角度,这样看起来就有抛物线的感觉。

4.遮罩层处理
在多层级的html渲染中,中间图层的遮罩效果无法实现。
解决方案:遮罩层可以在最底层使用,但是中间层级的遮罩效果需要对图片进行处理,改成png图片,再进行css操作。

5.卡顿的处理
在Firefox和ie中,小图标的缓慢移动效果会出现卡顿。
解决方案:给运动时间的时候,判断如果不是chrome浏览器,减小运动时间。

6.性能的优化
图片的使用让动画加载的速度变慢,影响用户体验。
解决方案:对部分能使用p代替的图片采用p生成,对代码,图片进行深度压缩上传等。


二、第二种项目风格

项目一:奕赏

项目描述:本项目是一款手机端APP,采用vue框架构建,其中涉及swiper触控滑动模块,slide子页之间采用了懒加载技术保证用户体验,iscroll上拉加载下拉刷新模块,购物车模块与登录注册模块均采用了本地存储技术。

岗位职责:主要负责页面的布局和数据渲染,并且配合APP完成页面的嵌套。

项目架构

1.使用vue框架,以及vue-router构建单页面应用。
2.项目采用vuex处理各组件间的通讯,vue-resource处理请求,使用mint-ui组件库中部分功能进行快速开发,通过vue-cli 快速搭建开发环境。
3.采用手机淘宝适配方案。
4.使用阿里矢量图标库。

技术要求

  1. 运用HTML5语义化标签+CSS3新特性进行页面布局,实现页面的动态效果,提高代码的清 晰度和代码质量,将页面体现的更加丰满,代码更健壮。
  2. 运用vue.js开发,采用前后端分离开发模式。
  3. 运用vue.js中的指令和服务与后台接口对接,进行数据交互,进行页面渲染,实现功能 模块的判断。
  4. 使用JavaScript实现某些功能的逻辑处理和某些页面的动态效果。
  5. 项目测试阶段可以自己用node连接数据库进行接口对接和数据渲染模拟,测试功能模块 是否完善,逻辑处理是否正确。
  6. 运用swiper框架进行部分页面的设计。
  7. 运用sass进行代码的编写,运用gulp进行代码的整理和压缩。

项目二:省钱日报

项目描述:本项目是一个促销商品推荐网站的移动APP,使用vue+webpack构建的单页面应用,项目采用了vuex、vue-route、vue-resource以及ES6语法,采用组件化思想搭建整个项目,从而使组件高度复用,代码十分简洁。

岗位职责:主要负责项目页面的布局和数据渲染,完成与后端的接口对接,配合后端的对接联调,解决不同浏览器或者不同手机端页面布局错乱的问题。

项目架构

1.使用vue框架,以及vue-router构建单页面应用。
2.使用vue+webpack构建项目环境。
3.采用手机淘宝适配方案。
4.使用阿里矢量图标库。

技术要求

1.项目采用node(express框架)+mysql来搭建后台服务器;
2.基于webpack来搭建项目工程,配置第三方插件;
3.使用vue框架,vue-router搭建项目路由,vuex来实现单文件组件和数据的抽离
4.使用Swiper实现首页banner的轮播切换,iscoll结合ajax实现上拉加载,下拉刷新
5.合理使用钩子函数,实现数据的监听、渲染页面、页面节点的实例化功能。

特色

写给前端应届生的职业规划建议

【前言】 近一年多的时间,笔者在公司中担任过应届生导师、实习生导师、应届生项目导师等一系列导师角色。期间,笔者发现一些即将和准备成为前端攻城狮的同学其实并没有明确前端真正的核心和定位,也没有给自己制定一个学习和发展的方向。当初选择前端这个职业,也仅仅只是为了找一份工作而已。笔者很想以自己的多年工作经验,为前端应届生们分享一下我心中适合前端工程师的职业规划,让大家对自己有一个比较清醒的认知,避免在以后的成长过程中产生迷茫情绪。当然,笔者其实也才工作五年,能达到现在的技术水平和职级,应该算是发展上升比较快的,一些经验具备一定的借鉴价值。

前端的兴起

前端真正兴起和开始频繁出现在大家的视线里,大概是在十年前。彼时的 Web 开发基本是由后端主导,前端能做的只是校验一下数据、操作一下 DOM。(其中数据检验是 JS 产生的根本原因:当时网络太慢,在服务端检验数据并反馈给用户,让用户知晓输入错误,这个流程太长、反应太慢,因此通过脚本在用户端完成第一步校验,既方便了用户,又减轻了带宽的压力。)即使 06 年 jQuery 发布并风靡全球,以及 XMLHttpRequest 被纳入 W3C 标准,也没有改变这种状况。制约它进度的原因很简单,因为很多事情前端 做不了 或者 做不好。随着时间的推移,近几年,Angular、Backbone、React、Vue 等框架陆续发布,让前端越来越正规化、体系化。此时虽然仍有很多事,前端 做不了 或者 做不好,但前端这个岗位却已变得热辣空前。那么,是什么推动着前端发展到如此大的规模和火热的程度?

也许,你可以列举出很多各种各样的原因,但是综其一点,就是 『用户体验』 ,是由于所有人对用户体验的重视,才让前端发展得这么迅猛,这么快地兴起。这里,可能要感谢 Apple,感谢 iPhone,感谢 Jobs,07 年第一代 iPhone 发布,正式引发了几乎所有人对用户体验的重视,从『只要能用就好』,变成『要好用我才买单』的心理。而,前端的先驱者们、浏览器的开发者们,也顺应了这个潮流,将一系列重要的能力加入了浏览器,加入了前端。

其中最重要的一项是 XMLHttpRequest,也就是 Ajax,它是富 Web 应用的基础,它让前端可以脱离后端的掌控,不用通过跳转的方式就能实现数据交互。感谢微软,感谢 IE,虽然被 IE 6~8 虐了无数遍,但是是它引入了 XMLHttpRequest ,引入了 Ajax,开启了富 Web 应用的时代,让用户体验大幅提升。

而近几年,随着移动互联网的发展,多端多平台的需求越来越多,产品形态和数据分离,是形势所趋。而与此同时,移动时代对产品形态跨端、跨平台、多元化的用户体验要求,让本身就有跨平台特性的前端技术着实又火了一把,它让开发者有更多的时间和精力关注用户体验,并很容易保持多平台用户体验的统一(不同平台用不同技术实现,虽然可以,但成本太高);除去上面的原因之外,前端技术自带的热发布、热更新特性,能在及时更新业务需求的同时快速修复用户所遇到的问题,也是大家选择它的一种原因。虽然另外还有很多零零总总、各种各样的原因,再促使着前端成为当前最火爆的几个职位之一,但是最根本的原因仍旧是大家对用户体验的要求。

从上面可以看出,前端的兴起源于所有人对 用户体验 的重视,而火爆更是由于所有人对 多元化的用户体验 的关注。当然,用户体验不只只是 UI 漂亮、好看,它是多方面的,例如视图的加载速度和流畅程度,这些取决于你选择技术的编码体积、运行效率等多种因素。说白了,前端的目的就是 让用户用得爽,那么 用户体验 必须是重中之重。

说了这么多,其实有一个很重要的点没有提到,那就是 CSS。注重用户体验,首先你要用界面要有 UI,HTML + CSS 作为最简单的 UI 构建方式,让前端的 UI 开发成本低到无与伦比,而开发成本低才会有更多的时间和精力去注重用户体验。同时,现在 CSS 也有相应的框架,像 PostCss、Sass 等,更进一步降低了开发成本,释放了开发者的时间和精力。

前端兴起这十年,也是用户体验飞速增长的十年。不管是技术完善度还是从业人数,前端这个方向受到了足够多技术人员的关注,同时也受到了足够多企业的重视。经过前端人不断的努力,现在的情况又如何呢?

前端的现状

提到现状,必须先提到一个概念 大前端。由于近几年互联网的发展,尤其是移动互联网的发展,有的大前端概念将 Native 归入前端的范畴,有的大前端概念将 Node 甚至只渲染页面的 PHP 归入前端范畴,但不管怎么说,笔者认为 大前端 是未来的一个趋势,将最终目标(提升用户体验)一致的技术归类到一起,让开发者清楚自己的最终目标是什么,要怎么做。当然,也正因为这点,作为一个前端工程师,如果你想更好的发展,你应该有更广的知识面,包括移动端知识、服务端知识。这些知识结合你的前端技术,才能更好地实现优秀的用户体验。

抛开大前端,单谈前端,从前端架构层面谈,最近比较流行的有四个:老牌劲旅 jQuery、最近火得不能再火的 React、Google 精品 Angular 以及 MVVM 框架 Vue。现在几乎所有的项目都会在这四种架构方案中选择其一作为基础,进行业务开发。四种框架,四种不同的思想,简单来说:jQuery Dom 驱动的思想深入人心;React 则推崇组件化,万物皆组件;Angular 则把 MVC 在前端领域发扬光大;而 Vue 则是以数据驱动为核心的 MVVM 架构。作为一个前端新人,不可能很快就理解所有知识和思想,只能一步一步来,先把你在工作中所使用的框架理解透彻,再去思考和学习别的。说实话,会用和理解的差距很大。

在这里,可能会有个疑问,上述四个架构,都很火,但是哪里涉及到用户体验了?是的,这些架构都没有直接涉及到 UI。但是就像足球,没有勤奋的训练和优秀的战术,再好的11人也踢不出好的比赛一样,这些架构从开发成本和开发体验上,降低了开发者编码和维护的难度,让其在 UI 的用户体验上的付出,事半功倍。当然,框架在编码体积、运行效率等多个方面影响了最终的用户体验。

上面所说的是,当前前端的一大现状 —— 框架横行,现在很少有公司、有工程师用纯原始的方式撸代码了。而前端另一大现状就是 —— 移动为先。原因很简单,随着移动互联网用户的暴涨,各个公司的产品都是移动为先,技术跟随着产品的步伐,也必须移动为先。这时,为了解决多平台的问题,Hybrid 方案脱颖而出,包括传统的基于 WebView 的 Hybrid 方案(例如 Cordova)和 React-Native 等一系列技术方案。在这里我就不多说了,关于移动前端的内容最近充斥着各种技术论坛、交流群、公共号,具体的,大家可以自己亲身去了解。

最后,对于现状,我想大家可能最关心的其实是职业形势。由于前端的兴起,前端人才市场相当活跃,平均薪金水平也是名列前茅。与此同时,前端的技术入门比较容易,造成另一个极端情况:人员泛滥、人才稀缺。这种情况,一方面由于前端发展太快,很难短时间掌握全部知识;另一方面,高等院校并没有开设专门的前端专业,大家更多是自学,野路子很多。所谓乱世出英雄,这样的前端大环境或许对一个新入行的同学更有利。当然,在如此『乱世』中,一个好的职业规划,才能避免『误入歧途』,保证自身顺利地成长。

如何做一个职业规划

上面讲述了前端如何兴起和前端的现状,下面将基于上述两点,分几个方面为大家提供一些有关职业规划的观点,希望对大家有帮助。

确定方向

做职业规划的目的是避免迷茫,而避免迷茫最有效的方式就是确定明确的方向和目标。

对于任何一个技术岗位,都有固定的两个方向:技术专家(架构师)和 开发经理。前者偏重技术,需要你在当前领域钻研得很深;后者偏向管理,需要你在对技术有很深掌握的同时,可以带领团队完成项目的开发。当然,两者并不是鱼与熊掌的关系,你可以同时成为技术专家和开发经理。

对于技术专家和开发经理两个方向的选择,更多取决于你自身在工作中多巴胺的分泌情况。当你专研技术时,多巴胺分泌得更多,感到更兴奋,或许你会很容易成为技术专家;反之,当你跟团队一起做业务时,多巴胺分泌得更多,更有获得感,那么你可以尝试向开发经理方向发展。当然,你也可能做什么都没有分泌太多的多巴胺,那么,你可以在尝试一段时间后,转型其他职业,例如产品经理。前端作为核心是用户体验,与用户最近的工程师,转型产品经理,阻碍会小一些。况且,文艺型前端布道人豆瓣前端负责人张克军认为,前端工程师正慢慢演变为产品工程师,前端和产品离得确实很近。

当你选择好一个方向后,你就要朝着这个方向一步一步进发。丹尼尔在《一万小时天才理论》提出一万小时定律,即要成为某个领域的专家,需要积累一万小时。当然这只是个概数,不过每天花更多的时间去学习和实践,肯定是最有效的。这里,成为技术专家和开发经理过程中,关注的点略有差别。成长为技术专家,要更多关注技术本身的实现,包括逻辑、架构、设计模式、方法论等;而成长为技术经理,则要更多关注技术开发的过程,考虑如何提高开发效率、降低开发成本、优化开发质量等等。不同的人,精力是有限的,选择性关注一些必要的方面,对自身快速的成长是很有必要的。

做业务还是做架构

做业务,时间要求比较紧,代码质量要求高,可参考的代码比较多,业务知识需要学习。做架构,时间稍微自由,对经验要求比较高,无可参考代码,专业基础知识需要深刻理解;最主要的,做架构的你既是开发,又是用户,还是 PM ,只有 80% – 90% 的明确目标,并在开发过程中不断微调最终的目标。

对于一个新人,其实不用纠结,做业务才是好的选择,而且做 技术含量高使用流行技术 的业务才是最好的。原因很简单,架构的最终的目的是解决业务当中的问题,你没做过业务,哪能知道业务的问题在哪,你都不知道要解决什么问题,如何做好架构。所以,从业务做起,是新人最好的选择,也是唯一可行的选择。而选择有技术含量、使用流行技术的业务的原因更多在于成长,这样你的成长可能会更快、成长道路可能会更直。当然,这只是『可能』,不同的人适合不同的业务,所以不要强求一定『技术含量高、使用流行技术』的业务,更多的而是改变自己,去 适应团队适应业务,这样才能 更快地成长

事实上,很多时候,你会遇到很业务工作很繁重没有额外时间学习的情况。而如何在这样环境中更快地成长呢?说白了就是『抄』,不不,是 参考。将学习融入到工作中,是最好的方法。做新项目,参考老项目代码;做新需求,参考老需求的代码;没有同类型的代码,参考别的业务的代码。参考前人的经验,在巨人的肩膀上,成长才会变得更快。同时,你的导师和你的伙伴,也会在业务中给你指点,帮你快速解决成长路上的问题。

在这里,总结一下,在繁重的业务环境下快速成长,你需要 很优秀的学习能力很持久的耐心 以及 很好的导师和伙伴,这样才能在技术成长的路上事半功倍。

技术的学习

说了半天,到了最核心的问题了,对于一个新人如何学习技术?笔者给的建议是:千万不要囫囵吞枣,先把当前使用的技术学透用熟,才是最重要的;千万不要在还没把当前使用的技术吃透之前,去学新的东西,不管新的东西有多火。就像上文所说,不同的框架,有不同的核心,有不同的思想。两个框架代码相似之处的思想不一定相似,例如 Angular 和 Vue 都有双向绑定,虽然效果相似,但是实现思想和内部实现方式是截然不同的。还在入门阶段的你,会被各种思想充斥头脑,反而会更不清楚。

一定的时间后,当你理解透一个架构体系后,你可以 类比地去看 更多的架构体系。这时候,你会发现不同架构很多东西都是殊途同归,理解得很快。

当然,理解透一个架构体系,有人需要一年,有人需要三年,还有人可能需要更长时间。为什么有这么大的区别呢?因为有些人在开发中,并不认为完成就可以了,会在开发中,追求代码的优美,会不断优化自己的代码,让自己的代码性能更好、可读性更高,并通过长时间的积累,达到 量变导致质变 的程度。即使一个特别聪明的人,没有『量』也不可能『质变』的,只不过他的量可能比其他人少而已。

要提醒的一点是,学技术,一定要结合你所在公司、团队的技术栈。例如,去哪儿前端应届生会在进入业务线前,进行3个月的脱产培训,2017年的前端培训课程内容中涉及的技术主要是 React 和 React Native,而去哪儿业务的技术栈也大多是 React,那么作为去哪儿的前端应届生,你优先学习 React 的技术体系是事半功倍的,既有前人可以问,又有项目可以实践。

当然,在学习架构的同时,不要忽略两样最基本的东西,一个是 技术基础,一个是 开发规范

技术基础是一切开发、架构的前提,没有一个好的基础,是无法让你自身的技术水平达到足够高的维度。例如你对于继承理解的并不透彻,你很难理解清晰 React 的内部实现。

对于开发规范,笔者在带应届生时特别注意让他们遵守。代码规范比比皆是,但是很少有人严格遵守。究其原因,多是在代码规范制定之前,已经有自己的一套代码习惯,很难短时间改变自己的习惯。而应届生,一般来说代码并不多,还没有形成自己的编码习惯。这时候,开始遵守一定的规范,会促使他们养成一个较好的编码习惯,为后续的成长打好基础。下面,列举一下开发规范的几点好处,让大家明白代码规范的重要性:

  • 规范的代码可以促进团队合作。
  • 规范的代码可以减少 Bug 处理。
  • 规范的代码可以降低维护成本。
  • 规范的代码有助于代码审查。
  • 养成代码规范的习惯,有助于程序员自身的成长。


这部分最后,推荐一些学习技术的好地方,例如情封大大三年不停更的《前端早读课》、阿里大漠(不是大漠穷秋)的 w3cplus.com、微信公众号《前端圈》、《前端之巅》、《Node 全栈》,当然还有公司内的 《Qunar 技术沙龙》微信公众号,笔者所在团队 YMFE 的博客 blog.ymfe.org 等,都是学习技术的好地方。

主战场 —— 移动混合开发

随着移动浪潮的兴起,业务在移动端App 的需求量迅速扩大,应用迭代更新的频率也随之极速攀升,但与此同时纯 Native 的开发和更新成本成为了业务增长难以逾越的瓶颈。因此,引入一种开发更高效、成本更低的解决方案势在必行。

在当前的移动互联网环境下,iOS 和 Android 上的 App 已经成了每个互联网产品的标配。如果一个用户端产品并不提供相应 App 版本,几乎会直接定义成一个不完整的产品。而被互联网人尊为铁律的『唯快不破』—— 快速开发、高速迭代、低成本上线,同时也是移动时代每个开发团队所追求的目标。综合以上两点原因,『Native 搭台,Web 唱戏』的 Hybrid 开发模式,以『快』的特点赢得了大家的青睐,并纷纷投入大量开发力量,使这种开发模式迅速走红。当前最常见的技术架构方案有以下三种:

  • 基于 Web 的 Hybrid 解决方案:例如微信浏览器、各公司的 Hybrid 方案
  • 非基于 Web UI 但业务逻辑基于 JavaScript 的解决方案:例如 React-Native
  • 基于 Web UI,但是为了追求运行效率,对 UI 展现逻辑和业务逻辑的 JavaScript 进行了隔离的解决方案:微信小程序

对于一个前端,笔者感觉每个人必须了解这三种常见方案的实现方式和优缺点,这样才能在开发移动端业务的时候,更为清楚自己所要注意、所要学习的地方。当然,仅仅了解实现方式是不够的,你要有环境去实践你学习的东西。再拿去哪儿为例,去哪儿现在大多数业务都是移动端的,Hybrid 和 RN 方案都在被使用。所以,作为一个应届生,你很有可能去做一些 Hybrid 或者 RN 的项目。做 Hybrid 项目时,你更多要考虑的是『如何高效地操作 Dom』;反之,做 RN 项目,你更多要考虑的则是『如何减少和 Native 的通信』。这两点,最终都会反应在项目的用户体验上。

前端中的『另类』—— Node

对于 Node,作为一个前端,应该并不陌生。Node 最大的卖点在于完全异步的 I/O 模型,相比于阻塞 I/O ,异步 I/O 模型极大提高 Web 服务的并发性。因此,前端都可以自己开发服务端了?

这样认为的同学,笔者只能说,你想多了。Node 是可以开发服务端,但是不代表所有前端都可以使用 Node 去开发一个庞大业务的服务端。你去知乎搜索使用 Node 开发服务端的相关问题,一部分人会说 Node 不能替代之前的服务端语言,另一部分人会说什么也阻挡不了 Node 在服务端的脚步;同时有很多诸如 Paypal、阿里这样大公司大规模使用 Node,也很多公司在落入 Node 深坑而不起。不论争论如何,笔者认为,Node 是否能写服务端,主要在于使用 Node 的人是否有服务端的思想。开发服务端和开发前端是完全不同的思想,服务端更注重效率、更注重稳定、更注重高并发情况下数据的处理,用前端的思想去开发服务端显然是不行的。当然,成功的案例中,Node 也更多运用在页面渲染这一层,配合前端更快的渲染页面,提高用户体验;而复杂的数据逻辑,还是用传统的服务端语言进行开发,毕竟技术成熟、运维成本低。

这里,会出现一个问题,我只是前端,需不需要去学习 Node?笔者的答案是 需要。前端兴起已经很多年,已经从游击队乱枪打鸟的阶段逐渐变为规模化、工程化的时代。在这个时代中,尤其是在工具和流程方面,Node 起到了很大的作用,扮演很重要的角色。诸如 Webpack、Gulp、NPM 这些工具,他们被运用在各个公司的各类前端项目中。学习 Node 其实就是去学习前端的工具,去学习前端的工程化。

当然,如果你立志是全栈,或者想做服务端的一些工作,再或者想通过 Node 学习服务端的技术和原理,都可以去学习 Node。请相信笔者,你不会失望的。

前端一直在『造轮子』中不断前进

造轮子 的意义是重新实现已有的功能,以达到更好的效果。这个词语,常见于技术圈,尤其是前端圈,逐渐被人贬义地用来『嘲讽』一个重新实现已有功能的工具、框架以及方案。那么,造轮子究竟是怎样一种行为?

首先,笔者并不排斥造轮子,至少有一位大神造轮子造得很成功,那就是 Linus Torvalds。Minix 不好用,就造了一个 Linux;Bitkeeper 停止授权,就造了一个 Git。并且两个新『轮子』已经成为世界上最好的轮子之二。这种创造性的造轮子,是令人赞赏的。

其次,不同的技术体系,互相学习,参考对方的思想,造自己的轮子,也是一个非常好的想法。尤其是前端,它通过学习其他语言、体系的架构方案,丰富自身,短短几年就上升到很高的高度。可以举得列子很多:例如 Angular 的 MVC + DI + IoC 的模式,很多人会直接想到 Java 的 Spring,甚至 Angular 也包含 AOP 的编程思想;再例如 MVVM 双向数据流的模式,本身就是微软的 WPF 首先提出的。轮子可以造,对开发有意义即可。

最后,大家 反感造轮子,其实更多在于反感造一个没有任何创新的轮子,还天天到处宣扬自己的产物。你可以自己造一个没有任何创新的轮子,用来学习造轮子的过程,同时这也是深入轮子最好的方式,没有之一。当你了解当前轮子的根本构造之后,你就能在这个基础上进行更多的创新。

其他方面的学习

作为一个前端,最该学习的两个方面是 产品 和 设计 。学习产品思维,会帮助你从用户的角度审查你开发的产品,找出用户体验不优、交互不好的地方;而学习设计,则帮助你分析不优的地方的原因,并找出解决方案。当然,每个人的精力有限,不可能把这两个方面学得特别深,但最好都要有接触,有一定深度的学习。

小总结

前文并没有直接跟大家说明怎么做职业规划,而是从发展方向、技术学习、主要业务场景、NodeJS、造轮子问题等多个方面,来给大家分析:作为前端,哪些需要做、哪些不需要做、哪些必须做、哪些可以做。大家可以根据自己的兴趣、能力等特性,并且和自己在公司所做的项目结合,决定做哪些、不做哪些、强制自己做哪些、有空余时间做哪些等等类似的决定。这里可以举个例子,例如:我的目标是成为一个技术专家,因此,我要深植业务一到两年,严格控制自己的代码规范,深入了解 React 体系,在移动端主攻 React Native,而且有时间看 Node 及 Babel 相关工具,目标是彻底理解 React 的技术实现和 Web Component 的开发思想。当你有这样一个目标时,你可以清楚知道自己做事的目的,可以清晰看到自己的成长。

要注意的是,如果你已经有工作的话,你的职业规划绝对不能脱离你现在的工作。实践出真知,工作带给你的实践环境,才是你成长的营养来源。

前端的未来

对于前端的未来,或者说是互联网技术的未来,我比较认同阮一峰老师的一个观点:将来程序员只分为两类,端工程师 和 云工程师。端工程师,直面用户,为了用户能更好使用产品而殚精竭虑,包括传统的前端、Native、AR/VR、TV 端开发等;而云工程师,直面数据,将冰冷冷的数据转化为与用户息息相关的产品,包括分布式、大数据、机器学习等等。

前端不管演变成什么,它所关注的永远是 用户体验,只不过端增加了,开发的方式也增多了而已。作为一个前端工程师,要时时关注业界动态,更多地学习新技术,提升自己的实力,来适应更大的挑战。

写在最后

作为一个初入前端圈的新人,你或许对上面描述的东西并不完全理解,这没有任何问题。做一个适合自己的职业规划,并不急于一时,其实最好是在工作一段时间后,和你的 leader 或者伙伴们一起对自己做一个评估,并根据前面所讲,给自己制定一个适合自身情况、符合自身兴趣的职业规划,并按照规划一步步不断提高自己,向前方的成功迈进。

笔者真心希望这篇文章可以给正在迷茫的你些许帮助。年轻且精力旺盛的前端领域,还没有形成像后端那样多的实施标准和最佳实践,每个人都可能有不同的思维、不同的思路。本文仅是笔者个人观点和看法,有错误或者疏漏之处,敬请谅解。同时也欢迎私下和笔者进行更深层次的探讨。

特色

谈前端工程师的职业规划

在敲下这个标题的时候,心里好虚。话说我一直不太喜欢发表这些看上去很假大空的文字,每个人的职业规划都是独有的,不具有太大的可复制性,把自己的经历放出去,容易误人子弟。只是最近很多师弟们(别问我为什么都是师弟,我想静静……也不要问我静静是谁!)问起这个,也就根据自己的经历发表一下对前端工程师的看法吧,“我说的都是错的”,仅供参考。另:本篇是纯文字,密集恐惧症换成勿入!

从我接收第一份前端需求开始,到现在也有五个年头了。自己也从一个愣头青变成一个快到而立之年的大叔,时间真的是哗啦哗啦的快。这五年里,其实可以分成三部分:1~2, 3~4, 5。

1~2 吸吸吸,疯狂的吸取知识

刚毕业时满腔热情,一门心思只往前端事业发展。刚从学校出来,看到什么都是新鲜十足,什么都想学。不断的买书看书垫枕头,最疯狂的时候一个礼拜晚上看完正本犀牛书+做笔记,后来再也达不到这种速度了。很幸运我能加入到AlloyTeam,依靠WebQQ,使得我的编码能力、项目经验duang duang duang的上去了。同时也搞了很多奇形怪状的业余项目,基本上无论遇到什么跟JS相关的“新”技术(新是对于我自己来说的),我都想去尝一尝。

对于刚毕业的初学者来说,头两年真的真的是非常重要,学习的激情、速度以及熬夜的能力可以发挥的淋漓尽致。在这个阶段,通过大量的编码提升JS能力,同时尝试各种JS框架和库,寻找适合自己的框架,甚至自己也写了一个JS库和构建工具。“不要重复的造轮子”这句话,初学者都应该无视。你不造轮子,怎么知道别的轮子好不好用?怎么知道造一个轮子有多难?怎么知道如何改进轮子?我不认为一个新手去看jQuery的源码能看懂,反正我当时就晕晕乎乎的。

在这个时间点里,其实还没怎么考虑以后的发展,而且也仅认为前端工程师,学好JS/CSS/HTML就够了,其他的没兴趣也没意愿去学。

3~4 大大大,扩展知识面

在JS/CSS/HTML里探索了2年后,突然发现越学越不懂了。出了HTML5,看了WebSocket,想学会,结果要看网络协议、要看HTTP/TCP,还要学Server开发等。顿时感觉分身10个也不一定能学好。为了跟上潮流,也得硬着头皮上。好不容易Server、网络、前端、浏览器各种知识都多少了解的时候,移动潮流来了!OMG,Android、iOS还有WP……生命不止,学习不休。

其实到了这个阶段,多少也算是个有经验的攻城师了。开发一般需求不在话下,偶尔还能承担个架构设计。在提升JS能力的同时,开始要往多方面发展。前端工程师远远不仅是JS而已。制作一个性能高、交互好、视觉美的页面,需要从前端框架选型、架构设计、构建工具,到后端通信机制、设计与交互、网络和浏览器优化等各方面的知识。一专多长才是前端工程师的终极目标。有个人说得对:一专是指你不可替代,多长标示你可以替代别人。这样你在团队在公司的地位才更加牢固,公司也会给你提供更多的资源。

同时还要开始提升自己的名气,可以通过发表技术博客,提交和参与开源项目,做部门级公司级还有业界分享,参加业界会议等等。在这个信息瞬息万变的互联网时代,保持交流才会让自己不落伍。同时名气大了,能找到更多志同道合的人一起研究技术(探讨生命的起源也不是不可能的),甚至想跳槽,也是放个屁的事儿(意思是简单-_-|| 这都什么比喻)。

5~ 摸摸摸,摸清前方的路

这个时候,如无意外,你也应该成为高级工程师了。编码在工作中比例已经不算太高,更多的工作是承担技术评审、架构设计和项目管理等事情。工作中除了完成自己的事情,可能还需要指导新人,做新人培训,带领新人完成项目。同时还会有一个抉择摆在眼前:继续深入研究技术 or 往管理方向进发。

技术 or 管理,这里没有对错之分,只有合不合适。有的人就不善于交流,同时喜欢专研,可以继续走技术的路,发展成专家;有的人则可能有比较强的领导力,可以带领一群人完成项目,那可以转向管理,成为Team Leader等。

不管选哪个方向,你都已经是个优秀的人,而且应该把这种优秀传承下去。成为导师,使新人变得优秀;成为面试官,挖掘优秀的人;成为讲师,让更多人学习到优秀,等等。

总的来说,头两年一定要打好基础,找准自己的G点,深攻下去;高潮之后,往广度发展,掌握其他相关和不相关的配套知识(这不是矛盾么?Σ( ° △ °|||)︴);然后摸到自己的目标,发售大胆的爱爱(老了,手抖打多了)吧。不仅自己爱(前端),也要让更多人一起爱(前端)哦。

特色

vue前端框架面试问题汇总,附答案

1、active-class是哪个组件的属性?嵌套路由怎么定义?
答:vue-router模块的router-link组件。
2、怎么定义vue-router的动态路由?怎么获取传过来的动态参数? 
答:在router目录下的index.js文件中,对path属性加上/:id。  使用router对象的params.id
3、vue-router有哪几种导航钩子?    
答:三种,一种是全局导航钩子:router.beforeEach(to,from,next),作用:跳转前进行判断拦截。第二种:组件内的钩子;第三种:单独路由独享组件
4、scss是什么?安装使用的步骤是?有哪几大特性?
答:预处理css,把css当前函数编写,定义变量,嵌套。 先装css-loader、node-loader、sass-loader等加载器模块,在webpack-base.config.js配置文件中加多一个拓展:extenstion,再加多一个模块:module里面test、loader
4.1、scss是什么?在vue.cli中的安装使用步骤是?有哪几大特性?
答:css的预编译。使用步骤:第一步:用npm 下三个loader(sass-loader、css-loader、node-sass)第二步:在build目录找到webpack.base.config.js,在那个extends属性中加一个拓展.scss

第三步:还是在同一个文件,配置一个module属性

第四步:然后在组件的style标签加上lang属性 ,例如:lang=”scss”

有哪几大特性:

1、可以用变量,例如($变量名称=值);

2、可以用混合器,例如()

3、可以嵌套
5、mint-ui是什么?怎么使用?说出至少三个组件使用方法?

答:基于vue的前端组件库。npm安装,然后import样式和js,vue.use(mintUi)全局引入。在单个组件局部引入:import {Toast} from ‘mint-ui’。组件一:Toast(‘登录成功’);组件二:mint-header;组件三:mint-swiper
6、v-model是什么?怎么使用? vue中标签怎么绑定事件?

答:可以实现双向绑定,指令(v-class、v-for、v-if、v-show、v-on)。vue的model层的data属性。绑定事件:<input @click=doLog() />
7、axios是什么?怎么使用?描述使用它实现登录功能的流程?

答:请求后台资源的模块。npm install axios -S装好,然后发送的是跨域,需在配置文件中config/index.js进行设置。后台如果是Tp5则定义一个资源路由。js中使用import进来,然后.get或.post。返回在.then函数中如果成功,失败则是在.catch函数中

 

8、axios+tp5进阶中,调用axios.post(‘api/user’)是进行的什么操作?axios.put(‘api/user/8′)呢?

答:跨域,添加用户操作,更新操作。
9、什么是RESTful API?怎么使用?

答:是一个api的标准,无状态请求。请求的路由地址是固定的,如果是tp5则先路由配置中把资源路由配置好。标准有:.post .put .delete
10、vuex是什么?怎么使用?哪种功能场景使用它?

答:vue框架中状态管理。在main.js引入store,注入。新建了一个目录store,….. export 。场景有:单页应用中,组件之间的状态。音乐播放、登录状态、加入购物车
11、mvvm框架是什么?它和其它框架(jquery)的区别是什么?哪些场景适合?

答:一个model+view+viewModel框架,数据模型model,viewModel连接两个

区别:vue数据驱动,通过数据来显示视图层而不是节点操作。

场景:数据操作比较多的场景,更加便捷
12、自定义指令(v-check、v-focus)的方法有哪些?它有哪些钩子函数?还有哪些钩子函数参数?

答:全局定义指令:在vue对象的directive方法里面有两个参数,一个是指令名称,另外一个是函数。组件内定义指令:directives

钩子函数:bind(绑定事件触发)、inserted(节点插入的时候触发)、update(组件内相关更新)

钩子函数参数:el、binding
13、说出至少4种vue当中的指令和它的用法?

答:v-if:判断是否隐藏;v-for:数据循环出来;v-bind:class:绑定一个属性;v-model:实现双向绑定
14、vue-router是什么?它有哪些组件?

答:vue用来写路由一个插件。router-link、router-view
15、导航钩子有哪些?它们有哪些参数?

答:导航钩子有:a/全局钩子和组件内独享的钩子。b/beforeRouteEnter、afterEnter、beforeRouterUpdate、beforeRouteLeave

参数:有to(去的那个路由)、from(离开的路由)、next(一定要用这个函数才能去到下一个路由,如果不用就拦截)最常用就这几种
16、Vue的双向数据绑定原理是什么?

答:vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的settergetter,在数据变动时发布消息给订阅者,触发相应的监听回调。

具体步骤:

第一步:需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上 settergetter
这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化

第二步:compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图

第三步:Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是:
1、在自身实例化时往属性订阅器(dep)里面添加自己
2、自身必须有一个update()方法
3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。

第四步:MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。

ps:16题答案同样适合”vue data是怎么实现的?”此面试题

17、请详细说下你对vue生命周期的理解?

答:总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后。

创建前/后: 在beforeCreated阶段,vue实例的挂载元素$el和数据对象data都为undefined,还未初始化。在created阶段,vue实例的数据对象data有了,$el还没有。

载入前/后:在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。

更新前/后:当data变化时,会触发beforeUpdate和updated方法。

销毁前/后:在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在
18、请说下封装 vue 组件的过程?

答:首先,组件可以提升整个项目的开发效率。能够把页面抽象成多个相对独立的模块,解决了我们传统项目开发:效率低难维护复用性等问题。

然后,使用Vue.extend方法创建一个组件,然后使用Vue.component方法注册组件。子组件需要数据,可以在props中接受定义。而子组件修改好数据后,想把数据传递给父组件。可以采用emit方法。
19、你是怎么认识vuex的?

答:vuex可以理解为一种开发模式或框架。比如PHP有thinkphp,java有spring等。
通过状态(数据源)集中管理驱动组件的变化(好比spring的IOC容器对bean进行集中管理)。

应用级的状态集中放在store中; 改变状态的方式是提交mutations,这是个同步的事物; 异步逻辑应该封装在action中。
20、vue-loader是什么?使用它的用途有哪些?

答:解析.vue文件的一个加载器,跟template/js/style转换成js模块。

用途:js可以写es6、style样式可以scss或less、template可以加jade等
21、请说出vue.cli项目中src目录每个文件夹和文件的用法?

答:assets文件夹是放静态资源;components是放组件;router是定义路由相关的配置;view视图;app.vue是一个应用主组件;main.js是入口文件
22、vue.cli中怎样使用自定义的组件?有遇到过哪些问题吗?

答:第一步:在components目录新建你的组件文件(smithButton.vue),script一定要export default {

第二步:在需要用的页面(组件)中导入:import smithButton from ‘../components/smithButton.vue’

第三步:注入到vue的子组件的components属性上面,components:{smithButton}

第四步:在template视图view中使用,<smith-button>  </smith-button>
问题有:smithButton命名,使用的时候则smith-button。
23、聊聊你对Vue.js的template编译的理解?

答:简而言之,就是先转化成AST树,再得到的render函数返回VNode(Vue的虚拟DOM节点)

详情步骤:

首先,通过compile编译器把template编译成AST语法树(abstract syntax tree 即 源代码的抽象语法结构的树状表现形式),compile是createCompiler的返回值,createCompiler是用以创建编译器的。另外compile还负责合并option。

然后,AST会经过generate(将AST语法树转化成render funtion字符串的过程)得到render函数,render的返回值是VNode,VNode是Vue的虚拟DOM节点,里面有(标签名、子节点、文本等等)

24、Vuex是什么?为什么使用Vuex?

答:Vuex 类似 Redux 的状态管理器,用来管理Vue的所有组件状态。

当你打算开发大型单页应用(SPA),会出现多个视图组件依赖同一个状态,来自不同视图的行为需要变更同一个状态。

25、vuejs与angularjs的区别?

答:
一、定位 :
虽然Vue.js被定义为MVC framework,但其实Vue本身还是一个library,加了一些其他的工具,可以被当成一个framework,而Angular 2虽然还是一个framework,但其实在设计之初,Angular 2的团队站在了更高的角度,希望做一个platform。

二、文档
vue.js的更加亲切

三、性能:
angular所有的数据和方法都是挂载在$scope上。而vue的数据和方法都是挂载在vue上,只是数据挂载在vue的data,方法挂载在vue的methods上,vue的代码风格更加优雅,json格式书写代码。Vue.js 有更好的性能,并且非常非常容易优化,因为它不使用脏检查。Angular,当 watcher 越来越多时会变得越来越慢,因为作用域内的每一次变化,所有 watcher 都要重新计算。

其它区别:
渲染性能:Vue> react >angular。
使用场景:Vue React 覆盖中小型,大型项目。angular 一般用于大型(因为比较厚重)

26、vue为什么不直接操作dom?

答:因为操作dom对象后,会触发一些浏览器行为,比如布局(layout)和绘制(paint)。  paint是一个耗时的过程,然而layout是一个更耗时的过程,我们无法确定layout一定是自上而下或是自下而上进行的,甚至一次layout会牵涉到整个文档布局的重新计算。浏览器的layout是lazy的,也就是说:在js脚本执行时,是不会去更新DOM的,任何对DOM的修改都会被暂存在一个队列中,在当前js的执行上下文完成执行后,会根据这个队列中的修改,进行一次layout。

27、你怎么理解vue是一个渐进式的框架?

答:我觉得渐进式就是不必一开始就用Vue所有的全家桶,可以根据场景,按需使用想要的插件。也可以说就使用vue不需要太多的要求。

28、Vue声明组件的state是用data方法,那为什么data是通过一个function来返回一个对象,而不是直接写一个对象呢?
答:从语法上说,如果不用function返回就会出现语法错误导致编译不通过。从原理上的话,大概就是组件可以被多次创建,如果不使用function就会使所有调用该组件的页面公用同一个数据域,这样就失去了组件的概念了

29、说下vue组件之间的通信?
答:
非父子组件间通信,Vue 有提供 Vuex,以状态共享方式来实现同信,对于这一点,应该注意考虑平衡,从整体设计角度去考量,确保引入她的必要。

父传子: this.$refs.xxx 子传父: this.$parent.xxx

还可以通过$emit方法出发一个消息,然后$on接收这个消息

30、vue中mixin与extend区别?
答:
全局注册混合对象,会影响到所有之后创建的vue实例,而Vue.extend是对单个实例进行扩展。

mixin 混合对象(组件复用)

同名钩子函数(bind,inserted,update,componentUpdate,unbind)将混合为一个数组,因此都将被调用,混合对象的钩子将在组件自身钩子之前调用

methods,components,directives将被混为同一个对象。两个对象的键名(方法名,属性名)冲突时,取组件(而非mixin)对象的键值对

挑战一下:
1、vue响应式原理?
2、vue-router实现原理?
3、为什么要选vue?与其它框架对比的优势和劣势?
4、vue如何实现父子组件通信,以及非父子组件通信?
5、vuejs与react的区别?
6、vuex是用来做什么的?
7、vue源码结构
8、vue路由,hash history是怎么监听的?
9、你使用过服务端渲染SSR吗?

不定项选择题:

  • 下列关于vue的组件说法不正确的是(    )
    1. 不一定要写style
    2. template视图里可以写多个div容器
    3. 父组件给子组件传值需定义props属性
    4. 子组件与父组件通信需定义$emit属性

 

  • 下面关于vue的声明周期说法不正确的是(    )
    1. 总共分为8个阶段:创建前/后、载入前/后、更新前/后、销毁前/后、
    2. updated和beforeUpdate分别是更新完成和更新前
    3. 创建后this才可以获取属性、mounted时$el节点才被渲染
    4. created创建后$el就不是undefined了
  • 下面关于js框架说法正确的是(    )
    1. Vue是一个MVVM框架
    2. Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件
    3. Vue中可以使用 v-for 指令来循环对象
    4. 在 input 输入框中我们可以使用 v-model 指令来实现双向数据绑定

Javascript面向对象编程(二):构造函数的继承

这个系列的第一部分,主要介绍了如何”封装”数据和方法,以及如何从原型对象生成实例。

今天要介绍的是,对象之间的”继承”的五种方法。

比如,现在有一个”动物”对象的构造函数。

function Animal(){

this.species = “动物”;

}

还有一个”猫”对象的构造函数。

function Cat(name,color){

this.name = name;

this.color = color;

}

怎样才能使”猫”继承”动物”呢?

一、 构造函数绑定

第一种方法也是最简单的方法,使用call或apply方法,将父对象的构造函数绑定在子对象上,即在子对象构造函数中加一行:

  function Cat(name,color){

Animal.apply(this, arguments);

this.name = name;

this.color = color;

}

var cat1 = new Cat(“大毛”,”黄色”);

alert(cat1.species); // 动物

二、 prototype模式

第二种方法更常见,使用prototype属性。

如果”猫”的prototype对象,指向一个Animal的实例,那么所有”猫”的实例,就能继承Animal了。

  Cat.prototype = new Animal();

Cat.prototype.constructor = Cat;

var cat1 = new Cat(“大毛”,”黄色”);

alert(cat1.species); // 动物

代码的第一行,我们将Cat的prototype对象指向一个Animal的实例。

  Cat.prototype = new Animal();

它相当于完全删除了prototype 对象原先的值,然后赋予一个新值。但是,第二行又是什么意思呢?

  Cat.prototype.constructor = Cat;

原来,任何一个prototype对象都有一个constructor属性,指向它的构造函数。如果没有”Cat.prototype = new Animal();”这一行,Cat.prototype.constructor是指向Cat的;加了这一行以后,Cat.prototype.constructor指向Animal。

  alert(Cat.prototype.constructor == Animal); //true

更重要的是,每一个实例也有一个constructor属性,默认调用prototype对象的constructor属性。

  alert(cat1.constructor == Cat.prototype.constructor); // true

因此,在运行”Cat.prototype = new Animal();”这一行之后,cat1.constructor也指向Animal!

  alert(cat1.constructor == Animal); // true

这显然会导致继承链的紊乱(cat1明明是用构造函数Cat生成的),因此我们必须手动纠正,将Cat.prototype对象的constructor值改为Cat。这就是第二行的意思。

这是很重要的一点,编程时务必要遵守。下文都遵循这一点,即如果替换了prototype对象,

  o.prototype = {};

那么,下一步必然是为新的prototype对象加上constructor属性,并将这个属性指回原来的构造函数。

  o.prototype.constructor = o;

三、 直接继承prototype

第三种方法是对第二种方法的改进。由于Animal对象中,不变的属性都可以直接写入Animal.prototype。所以,我们也可以让Cat()跳过 Animal(),直接继承Animal.prototype。

现在,我们先将Animal对象改写:

  function Animal(){ }

Animal.prototype.species = “动物”;

然后,将Cat的prototype对象,然后指向Animal的prototype对象,这样就完成了继承。

  Cat.prototype = Animal.prototype;

Cat.prototype.constructor = Cat;

var cat1 = new Cat(“大毛”,”黄色”);

alert(cat1.species); // 动物

与前一种方法相比,这样做的优点是效率比较高(不用执行和建立Animal的实例了),比较省内存。缺点是 Cat.prototype和Animal.prototype现在指向了同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype。

所以,上面这一段代码其实是有问题的。请看第二行

  Cat.prototype.constructor = Cat;

这一句实际上把Animal.prototype对象的constructor属性也改掉了!

  alert(Animal.prototype.constructor); // Cat

四、 利用空对象作为中介

由于”直接继承prototype”存在上述的缺点,所以就有第四种方法,利用一个空对象作为中介。

  var F = function(){};

F.prototype = Animal.prototype;

Cat.prototype = new F();

Cat.prototype.constructor = Cat;

F是空对象,所以几乎不占内存。这时,修改Cat的prototype对象,就不会影响到Animal的prototype对象。

  alert(Animal.prototype.constructor); // Animal

我们将上面的方法,封装成一个函数,便于使用。

  function extend(Child, Parent) {

var F = function(){};

F.prototype = Parent.prototype;

Child.prototype = new F();

Child.prototype.constructor = Child;

Child.uber = Parent.prototype;

}

使用的时候,方法如下

  extend(Cat,Animal);

var cat1 = new Cat(“大毛”,”黄色”);

alert(cat1.species); // 动物

这个extend函数,就是YUI库如何实现继承的方法。

另外,说明一点,函数体最后一行

  Child.uber = Parent.prototype;

意思是为子对象设一个uber属性,这个属性直接指向父对象的prototype属性。(uber是一个德语词,意思是”向上”、”上一层”。)这等于在子对象上打开一条通道,可以直接调用父对象的方法。这一行放在这里,只是为了实现继承的完备性,纯属备用性质。

五、 拷贝继承

上面是采用prototype对象,实现继承。我们也可以换一种思路,纯粹采用”拷贝”方法实现继承。简单说,如果把父对象的所有属性和方法,拷贝进子对象,不也能够实现继承吗?这样我们就有了第五种方法。

首先,还是把Animal的所有不变属性,都放到它的prototype对象上。

  function Animal(){}

Animal.prototype.species = “动物”;

然后,再写一个函数,实现属性拷贝的目的。

  function extend2(Child, Parent) {

var p = Parent.prototype;

var c = Child.prototype;

for (var i in p) {

c[i] = p[i];

}

c.uber = p;

}

这个函数的作用,就是将父对象的prototype对象中的属性,一一拷贝给Child对象的prototype对象。

使用的时候,这样写:

  extend2(Cat, Animal);

var cat1 = new Cat(“大毛”,”黄色”);

alert(cat1.species); // 动物

(本系列未完,请继续阅读第三部分《非构造函数的继承》。)

(完)

Javascript 面向对象编程(一):封装

Javascript是一种基于对象(object-based)的语言,你遇到的所有东西几乎都是对象。但是,它又不是一种真正的面向对象编程(OOP)语言,因为它的语法中没有class(类)。

那么,如果我们要把”属性”(property)和”方法”(method),封装成一个对象,甚至要从原型对象生成一个实例对象,我们应该怎么做呢?

一、 生成实例对象的原始模式

假定我们把猫看成一个对象,它有”名字”和”颜色”两个属性。

  var Cat = {    name : ”,

color : ”

}

现在,我们需要根据这个原型对象的规格(schema),生成两个实例对象。

  var cat1 = {}; // 创建一个空对象

cat1.name = “大毛”; // 按照原型对象的属性赋值

cat1.color = “黄色”;

var cat2 = {};

cat2.name = “二毛”;

cat2.color = “黑色”;

好了,这就是最简单的封装了,把两个属性封装在一个对象里面。但是,这样的写法有两个缺点,一是如果多生成几个实例,写起来就非常麻烦;二是实例与原型之间,没有任何办法,可以看出有什么联系。

二、 原始模式的改进

我们可以写一个函数,解决代码重复的问题。

  function Cat(name,color) {

return {

name:name,

color:color

}

}

然后生成实例对象,就等于是在调用函数:

  var cat1 = Cat(“大毛”,”黄色”);

var cat2 = Cat(“二毛”,”黑色”);

这种方法的问题依然是,cat1cat2之间没有内在的联系,不能反映出它们是同一个原型对象的实例。

三、 构造函数模式

为了解决从原型对象生成实例的问题,Javascript提供了一个构造函数(Constructor)模式。

所谓”构造函数”,其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。

比如,猫的原型对象现在可以这样写,

  function Cat(name,color){

this.name=name;

this.color=color;

}

我们现在就可以生成实例对象了。

  var cat1 = new Cat(“大毛”,”黄色”);

var cat2 = new Cat(“二毛”,”黑色”);

alert(cat1.name); // 大毛

alert(cat1.color); // 黄色

这时cat1cat2会自动含有一个constructor属性,指向它们的构造函数。

  alert(cat1.constructor == Cat); //true

alert(cat2.constructor == Cat); //true

Javascript还提供了一个instanceof运算符,验证原型对象与实例对象之间的关系。

  alert(cat1 instanceof Cat); //true

alert(cat2 instanceof Cat); //true

四、构造函数模式的问题

构造函数方法很好用,但是存在一个浪费内存的问题。

请看,我们现在为Cat对象添加一个不变的属性type(种类),再添加一个方法eat(吃)。那么,原型对象Cat就变成了下面这样:

  function Cat(name,color){

this.name = name;

this.color = color;

this.type = “猫科动物”;

this.eat = function(){alert(“吃老鼠”);};

}

还是采用同样的方法,生成实例:

  var cat1 = new Cat(“大毛”,”黄色”);

var cat2 = new Cat (“二毛”,”黑色”);

alert(cat1.type); // 猫科动物

cat1.eat(); // 吃老鼠

表面上好像没什么问题,但是实际上这样做,有一个很大的弊端。那就是对于每一个实例对象,type属性和eat()方法都是一模一样的内容,每一次生成一个实例,都必须为重复的内容,多占用一些内存。这样既不环保,也缺乏效率。

  alert(cat1.eat == cat2.eat); //false

能不能让type属性和eat()方法在内存中只生成一次,然后所有实例都指向那个内存地址呢?回答是可以的。

五、 Prototype模式

Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。

这意味着,我们可以把那些不变的属性和方法,直接定义在prototype对象上。

  function Cat(name,color){

this.name = name;

this.color = color;

}

Cat.prototype.type = “猫科动物”;

Cat.prototype.eat = function(){alert(“吃老鼠”)};

然后,生成实例。

  var cat1 = new Cat(“大毛”,”黄色”);

var cat2 = new Cat(“二毛”,”黑色”);

alert(cat1.type); // 猫科动物

cat1.eat(); // 吃老鼠

这时所有实例的type属性和eat()方法,其实都是同一个内存地址,指向prototype对象,因此就提高了运行效率。

  alert(cat1.eat == cat2.eat); //true

六、 Prototype模式的验证方法

为了配合prototype属性,Javascript定义了一些辅助方法,帮助我们使用它。,

6.1 isPrototypeOf()

这个方法用来判断,某个proptotype对象和某个实例之间的关系。

  alert(Cat.prototype.isPrototypeOf(cat1)); //true

alert(Cat.prototype.isPrototypeOf(cat2)); //true

6.2 hasOwnProperty()

每个实例对象都有一个hasOwnProperty()方法,用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性。

  alert(cat1.hasOwnProperty(“name”)); // true

alert(cat1.hasOwnProperty(“type”)); // false

6.3 in运算符

in运算符可以用来判断,某个实例是否含有某个属性,不管是不是本地属性。

  alert(“name” in cat1); // true

alert(“type” in cat1); // true

in运算符还可以用来遍历某个对象的所有属性。

  for(var prop in cat1) { alert(“cat1[“+prop+”]=”+cat1[prop]); }

未完,请继续阅读这个系列的第二部分《构造函数的继承》和第三部分《非构造函数的继承》

(完)

每个 JavaScript 工程师都应当知道的 10 个面试题

原文链接:10 Interview Questions Every JavaScript Developer Should Know


对大部分公司来说,招聘技术人员这种事情,管理层就应该放手交给技术团队,只有他们才能够准确地判断应聘者的技术实力。如果你恰巧是应聘者,你也是迟早都要去面试的。不管你是哪边的,都让大哥来教你几招。

大兄弟们,要收藏,也要点赞呐。

以人为本

How to Build a High Velocity Development Team 一文中,我提出了一些观点,我觉得这些观点很重要,所以在这里再重复一遍:

优秀的团队才是决定公司业绩的关键,一家公司要想于逆境之中仍能有所建树,最重要的就是得先培养出一只优秀的团队。

就像 Marcus Lemonis 说的,有三点(3 个 P)最重要:

员工(People),流程(Process),产品(Product)。

在创业初期,你招来的工程师必须是能够独当一面的大神队友。他最好能够帮着招聘工程师,能指导其它工程师,还能帮初级和中级工程师解决各种问题。这样优秀的队友,无论何时都多多益善。

要想知道面试应聘者时,有哪些常见的注意事项,可以读读 Why Hiring is So Hard in Tech 这篇文章。

要评估一个应聘者的真实水准,最佳方式就是结对编程(pair programming)。

和应聘者结对编程,一切都听应聘者的。多观察、多聆听,看看应聘者是个怎样的人。用微博的 API 抓取消息并显示在时间线上,就是个很好的考察应聘者的面试项目。

不过结对编程再好使,也没办法让你完全了解一个应聘者。这个时候,面试也能帮上很多忙——但是千万别浪费时间去问一些语法(syntax)或者语言上的细节(language quirks)——问些高端的问题吧,大兄弟。问问项目架构(architecture),编程范式(paradigms),这个层面上的判断(the big desicions)能够在很大程度上影响一个项目的成败。

语法和语言特性(features)这种小知识,Google 一搜一大把,谁都会。而工程师在工作中所积累的软件工程方面的经验,以及个人常用的编程范式及代码风格(idioms),这些可都是很难 Google 到的宝贵财富。

JavaScript 很独特,它在各种大型项目中都起着至关重要的作用。那是什么让 JavaScript 如此与众不同?

下面几个问题,也许能帮你一探究竟。


1. 能说出来两种对于 JavaScript 工程师很重要的编程范式么?

JavaScript 是一门多范式(multi-paradigm)的编程语言,它既支持命令式(imperative)/面向过程(procedural)编程,也支持面向对象编程(OOP,Object-Oriented Programming),还支持函数式编程(functional programming)。JavaScript 所支持的面向对象编程包括原型继承(prototypal inheritance)。

面试加分项

  • 原型继承(即:原型,OLOO——链接到其它对象的对象);
  • 函数式编程(即:闭包(closure),一类函数(first class functions),lambda 函数:箭头函数)。

面试减分项

  • 连范式都不知道,更别提什么原型 OO(prototypal oo)或者函数式编程了。

深入了解

2. 什么是函数式编程?

函数式编程,是将数学函数组合起来,并且避免了状态共享(shared state)及可变数据(mutable data),由此而产生的编程语言。发明于 1958 年的 Lisp 就是首批支持函数式编程的语言之一,而 λ 演算(lambda calculus)则可以说是孕育了这门语言。即使在今天,Lisp 这个家族的编程语言应用范围依然很广。

函数式编程可是 JavaScript 语言中非常重要的一个概念(它可是 JavaScript 的两大支柱之一)。ES5 规范中就增加了很多常用的函数式工具。

面试加分项

  • 纯函数(pure functions)/函数的纯粹性(function purity)
  • 知道如何避免副作用(side-effects)
  • 简单函数的组合
  • 函数式编程语言:Lisp,ML,Haskell,Erlang,Clojure,Elm,F#,OCaml,等等
  • 提到了 JavaScript 语言中支持函数式编程(FP)的特性:一类函数,高阶函数(higher order functions),作为参数(arguments)/值(values)的函数

面试减分项

  • 没有提到纯函数,以及如何避免副作用
  • 没有提供函数式编程语言的例子
  • 没有说是 JavaScript 中的哪些特性使得函数式编程得以实现

深入了解

3. 类继承和原型继承有什么区别?

类继承(Class Inheritance):实例(instances)由类继承而来(类和实例的关系,可以类比为建筑图纸和实际建筑 🏠 的关系),同时还会创建父类—子类这样一种关系,也叫做类的分层分类(hierarchical class taxonomies)。通常是用new 关键字调用类的构造函数(constructor functions)来创建实例的。不过在 ES6 中,要继承一个类,不用 class关键字也可以。

原型继承(Prototypal Inheritance):实例/对象直接从其它对象继承而来,创建实例的话,往往用工厂函数(factory functions)或者 Object.create() 方法。实例可以从多个不同的对象组合而来,这样就能选择性地继承了。

在 JavaScript 中,原型继承比类继承更简单,也更灵活。

面试加分项

  • 类:会创建紧密的耦合,或者说层级结构(hierarchies)/分类(taxonomies)。
  • 原型:提到了衔接继承(concatenative inheritance)、原型委托( prototype delegation)、函数继承(functional inheritance),以及对象组合(object composition)。

面试减分项

  • 原型继承和组合,与类继承相比,不知道哪个更好。

深入了解

4. 函数式编程和面向对象编程,各有什么优点和不足呢?

面向对象编程的优点:关于“对象”的一些基础概念理解起来比较容易,方法调用的含义也好解释。面向对象编程通常使用命令式的编码风格,声明式(declarative style)的用得比较少。这样的代码读起来,像是一组直接的、计算机很容易就能遵循的指令。

面向对象编程的不足:面向对象编程往往需要共享状态。对象及其行为常常会添加到同一个实体上,这样一来,如果一堆函数都要访问这个实体,而且这些函数的执行顺序不确定的话,很可能就会出乱子了,比如竞争条件(race conditions)这种现象(函数 A 依赖于实体的某个属性,但是在 A 访问属性之前,属性已经被函数 B 修改了,那么函数 A 在使用属性的时候,很可能就得不到预期的结果)。

函数式编程的优点:用函数式范式来编程,就不需要担心共享状态或者副作用了。这样就避免了几个函数在调用同一批资源时可能产生的 bug 了。拥有了“无参风格”(point-free style,也叫隐式编程)之类的特性之后,函数式编程就大大简化了,我们也可以用函数式编程的方式来把代码组合成复用性更强的代码了,面向对象编程可做不到这一点。

函数式编程更偏爱声明式、符号式(denotational style)的编码风格,这样的代码,并不是那种为了实现某种目的而需要按部就班地执行的一大堆指令,而是关注宏观上要做什么。至于具体应该怎么做,就都隐藏在函数内部了。这样一来,要是想重构代码、优化性能,那就大有可为了。(译者注:以做一道菜为例,就是由 买菜 -> 洗菜 -> 炒菜 这三步组成,每一步都是函数式编程的一个函数,不管做什么菜,这个流程都是不会变的。而想要优化这个过程,自然就是要深入每一步之中了。这样不管内部如何重构、优化,整体的流程并不会变,这就是函数式编程的好处。)甚至可以把一种算法换成另一种更高效的算法,同时还基本不需要修改代码(比如把及早求值策略(eager evaluation)替换为惰性求值策略(lazy evaluation))。

利用纯函数进行的计算,可以很方便地扩展到多处理器环境下,或者应用到分布式计算集群上,同时还不用担心线程资源冲突、竞争条件之类的问题。

函数式编程的不足:代码如果过度利用了函数式的编程特性(如无参风格、大量方法的组合),就会影响其可读性,从而简洁度有余、易读性不足。

大部分工程师还是更熟悉面向对象编程、命令式编程,对于刚接触函数式编程的人来说,即使只是这个领域的一些的简单术语,都可能让他怀疑人生。

函数式编程的学习曲线更陡峭,因为面向对象编程太普及了,学习资料太多了。相比而言,函数式编程在学术领域的应用更广泛一些,在工业界的应用稍逊一筹,自然也就不那么“平易近人”了。在探讨函数式编程时,人们往往用 λ 演算、代数、范畴学等学科的专业术语和专业符号来描述相关的概念,那么其他人想要入门函数式编程的话,就得先把这些领域的基础知识搞明白,能不让人头大么。

面试加分项

  • 共享状态的缺点、资源竞争、等等(面向对象编程)
  • 函数式编程能够极大地简化应用开发
  • 面向对象编程和函数式编程学习曲线的不同
  • 两种编程方式各自的不足之处,以及对代码后期维护带来的影响
  • 函数式风格的代码库,学习曲线会很陡峭
  • 面向对象编程风格的代码库,修改起来很难,很容易出问题(和水平相当的函数式风格的代码相比)
  • 不可变性(immutability),能够极大地提升程序历史状态(program state history)的可见性(accessible)和扩展性(malleable),这样一来,想要添加诸如无限撤销/重做、倒带/回放、可后退的调试之类的功能的话,就简单多了。不管是面向对象编程还是函数式编程,这两种范式都能实现不可变性,但是要用面向对象来实现的话,共享状态对象的数量就会剧增,代码也会变得复杂很多。

面试减分项

  • 没有讲这两种编程范式的缺点——如果熟悉至少其中一种范式的话,应该能够说出很多这种范式的缺点吧。

深入了解

总是你俩,看来你俩真是非常重要啊。

5. 什么时候该用类继承?

千万别用类继承!或者说尽量别用。如果非要用,就只用它继承一级(one level)就好了,多级的类继承简直就是反模式的。这个话题(不太明白是关于什么的……)我也参与讨论过好些年了,仅有的一些回答最终也沦为 常见的误解 之一。更多的时候,这个话题讨论着讨论着就没动静了。

如果一个特性有时候很有用
但有时候又很危险
并且还有另一种更好的特性可以用
务必要用另一种更好的特性
~ Douglas Crockford

面试加分项

  • 尽量别用,甚至是彻底不用类继承。
  • 有时候只继承一级的话也还是 OK 的,比如从框架的基类继承,例如 React.Component
  • 相比类继承,对象组合(object composition)更好一些。

深入了解

6. 什么时候该用原型继承?

原型继承可以分为下面几类:

  • 委托(delegation,也就是原型链)
  • 组合(concatenative,比如混用(mixins)、Object.assign()
  • 函数式(functional,这个函数式原型继承不是函数式编程。这里的函数是用来创建一个闭包,以实现私有状态(private state)或者封装(encapsulation))

上面这三种原型继承都有各自的适用场景,不过它们都很有用,因为都能实现组合继承(composition),也就是建立了 A 拥有特性 B(has-a)、A 用到了特性 B(uses-a) 或者 A 可以实现特性 B(can-do) 的这样一种关系。相比而言,类继承建立的是 A 就是 B 这样一种关系。

面试加分项

  • 知道在什么情况下不适合用模块化(modules)或者函数式编程。
  • 知道需要组合多个不同来源的对象时,应该怎么做。
  • 知道什么时候该用继承。

面试减分项

  • 不知道什么时候应该用原型。
  • 不知道混用和 Object.assign()

深入了解

7. 为什么说“对象组合比类继承更好”?

这句话引用的是《设计花纹》(Design Patterns,设计模式)这本书的内容。意思是要想实现代码重用,就应该把一堆小的功能单元组合成满足需求的各种对象,而不是通过类继承弄出来一层一层的对象。

换句话说,就是尽量编程实现 can-dohas-a 或者 uses-a 这种关系,而不是 is-a 这种关系。

面试加分项

  • 避免使用类继承。
  • 避免使用问题多多的基类。
  • 避免紧耦合。
  • 避免极其不灵活的层次分类(taxonomy)(类继承所产生的 is-a 关系可能会导致很多误用的情况)
  • 避免大猩猩香蕉问题(“你只是想要一根香蕉,结果最后却整出来一只拿着香蕉的大猩猩,还有整个丛林”)。
  • 要让代码更具扩展性。

面试减分项

  • 没有提到上面任何一种问题。
  • 没有表达清楚对象组合与类继承有什么区别,也没有提到对象组合的优点。

深入了解

8. 双向数据绑定/单向数据流的含义和区别

双向数据绑定(two-way data binding),意味着 UI 层所呈现的内容和 Model 层的数据动态地绑定在一起了,其中一个发生了变化,就会立刻反映在另一个上。比如用户在前端页面的表单控件中输入了一个值,Model 层对应该控件的变量就会立刻更新为用户所输入的值;反之亦然,如果 Modal 层的数据有变化,变化后的数据也会立刻反映至 UI 层。

单向数据流(one-way data flow), 意味着只有 Model 层才是单一数据源(single source of truth)。UI 层的变化会触发对应的消息机制,告知 Model 层用户的目的(对应 React 的 store)。只有 Model 层才有更改应用状态的权限,这样一来,数据永远都是单向流动的,也就更容易了解应用的状态是如何变化的。

采用单向数据流的应用,其状态的变化是很容易跟踪的,采用双向数据绑定的应用,就很难跟踪并理解状态的变化了。

面试加分项

  • React 是单向数据流的典型,面试时提到这个框架的话会加分。Cycle.js 则是另一个很流行的单向数据流的库。
  • Angular 则是双向数据绑定的典型。

面试减分项

  • 不理解单向数据流/双向数据绑定的含义,也说不清楚两者之间的区别。

深入了解

9. 单体架构和微服务架构各有何优劣?

采用单体架构(monolithic architecture)的应用,各组件的代码是作为一个整体存在的,组件之间互相合作,共享内存和资源。

而微服务架构(microservice architecture)则是由许许多多个互相独立的小应用组成,每个应用都有自己的内存空间,应用在扩容时也是独立于其它应用进行的。

单体架构的优势:大部分应用都有相当数量的横切关注点(cross-cutting concerns),比如日志记录,流量限制,还有审计跟踪和 DOS 防护等安全方面的需求,单体架构在这方面就很有优势。

当所有功能都运行在一个应用里的时候,就可以很方便地将组件与横切关注点相关联。

单体架构也有性能上的优势,毕竟访问共享内存还是比进程间通信(inter-process communication,IPC)要快的。

单体架构的劣势:随着单体架构应用功能的不断开发,各项服务之间的耦合程度也会不断增加,这样一来就很难把各项服务分离开来了,要做独立扩容或者代码维护也就更不方便了。

微服务的优势:微服务架构一般都有更好的组织结构,因为每项服务都有自己特定的分工,而且也不会干涉其它组件所负责的部分。服务解耦之后,想要重新组合、配置来为各个不同的应用提供服务的话,也更方便了(比如同时为 Web 客户端和公共 API 提供服务)。

如果用合理的架构来部署微服务的话,它在性能上也是很有优势的,因为这样一来,就可以很轻松地分离热门服务,对其进行扩容,同时还不会影响到应用中的其它部分。

微服务的劣势:在实际构建一个新的微服务架构的时候,会遇到很多在设计阶段没有预料到的横切关注点。如果是单体架构应用的话就很简单,新建一个中间件(shared magic helpers 不知道怎么翻译……)来解决这样的问题就行了,没什么麻烦的。

但是在微服务架构中就不一样了,要解决这个问题,要么为每个横切关注点都引入一个独立的模块,要么就把所有横切关注点的解决方案封装到一个服务层中,让所有流量都从这里走一遍就行了。

为了解决横切关注点的问题,虽然单体架构也趋向于把所有的路由流量都从一个外部服务层走一遍,但是在这种架构中,可以等到项目非常成熟之后再进行这种改造,这样就可以把还这笔技术债的时间尽量往后拖一拖。

微服务一般都是部署在虚拟机或容器上的,随着应用规模的不断增加,虚拟机抢工作(VM wrangling work)的情况也会迅速增加。任务的分配一般都是通过容器群(container fleet)管理工具来自动实现的。

面试加分项

  • 对于微服务的积极态度,虽然初始成本会比单体架构要高一些。知道微服务的性能和扩容在长期看来表现更佳。
  • 在微服务架构和单体架构应用上都有实战经验。能够使应用中的各项服务在代码层面互相独立,但是又可以在开发初期迅速地将各项服务打包成一整个的单体架构应用。微服务化的改造可以在应用相当成熟之后,改造成本在可承受范围内的时候再进行。

面试减分项

  • 不知道单体架构和微服务架构的区别。
  • 不知道微服务架构额外的开销,或者没有实际经验。
  • 不知道微服务架构中,IPC 和网络通信所导致的额外的性能开销。
  • 过分贬低微服务。说不清楚什么时候应该把单体架构应用解耦成微服务。
  • 低估了可独立扩容的微服务的优势。

10. 异步编程是什么?又为什么在 JavaScript 中这么重要?

在同步编程中,代码会按顺序自顶向下依次执行(条件语句和函数调用除外),如果遇到网络请求或者磁盘读/写(I/O)这类耗时的任务,就会堵塞在这样的地方。

在异步编程中,JS 运行在事件循环(event loop)中。当需要执行一个阻塞操作(blocking operation)时,主线程发起一个(异步)请求,(工作线程就会去执行这个异步操作,)同时主线程继续执行后面的代码。(工作线程执行完毕之后,)就会发起响应,触发中断(interrupt),执行事件处理程序(event handler),执行完后主线程继续往后走。这样一来,一个程序线程就可以处理大量的并发操作了。

用户界面(user interface,UI)天然就是异步的,大部分时间它都在等待用户输入,从而中断事件循环,触发事件处理程序。

Node.js 默认是异步的,采用它构建的服务端和用户界面的执行机制差不多,在事件循环中等待网络请求,然后一个接一个地处理这些请求。

异步在 JavaScript 中非常重要,因为它既适合编写 UI,在服务端也有上佳的性能表现。

面试加分项

  • 理解阻塞的含义,以及对性能带来的影响。
  • 理解事件处理程序,以及它为什么对 UI 部分的代码很重要。

面试减分项

  • 不熟悉同步、异步的概念。
  • 讲不清楚异步代码和 UI 代码的性能影响,也说不明白它俩之间的关系。

总结

多问问应聘者高层次的知识点,如果能讲清楚这些概念,就说明即使应聘者没怎么接触过 JavaScript,也能够在短短几个星期之内就把语言细节和语法之类的东西弄清楚。

不要因为应聘者在一些简单的知识上表现不佳就把对方 pass 掉,比如经典的 CS-101 算法课,或者一些解谜类的题目。

面试官真正应该关注的,是应聘者是否知道如何把一堆功能组织在一起,形成一个完整的应用。

电话面试的注意点就这些了,在线下的面试中,我更加关注应聘者实际编写代码的能力,我会观察他如何写代码。在我的《精通 JavaScript 面试》这个系列文章中,会有更深入的描述。

记一年前端小白面经

起点

从去年2017年7月毕业至今将近快1年的时间了,2018年5月底6月初开始面试,一共面了快手,滴滴,头条。很幸运每家都面完了3轮技术+1轮hr,能跟这么多大牛聊天真是太有收获了,对基础是一次查漏补缺,对技术视野也是一次提升,于是就想记录一下这些面试的问题与收获,有一些问题值得更加深入的研究的。

问题

由于已经过了些时日,仅记录问题而不分哪家也不分几轮面试。

  • 计算机基础类
    • TCP和UDP区别以及各自的应用
    • TCP3次握手与4次挥手,为什么要这么做
    • keep-alive是对应接口复用还是对应的域名复用
    • HTTP与HTTPS区别,HTTPS握手过程,包含对称非对称加密,在哪步做,为什么要包含两种加密
    • 是否了解HTTP2
    • 进程与线程的区别
    • 物理地址与虚拟地址
    • websocket协议握手以及对nignx需要进行什么配置
  • 智力类
    • 海盗分金
    • 概率论抽扑克牌问题
  • 算法类
    • 快排算法,并说出你写快排的时间与空间复杂度(如果是阮大神快排就要注意空间复杂度了)
    • 递归反转二叉树
    • 检测字符串回文,并说出时间与空间复杂度
    • 写一个函数,简化linux相对路径(如 /a/b/.././c => /a/c)
  • 前端类
    • JS的六大基本类型以及判断方法(5+symbol)
    • JS判断数组以及对象的方法
      即 typeof instanceof Object.prototype.toString.call 适用范围
    • apply和call的区别
    • 原生bind的作用 实现一个bind的polyfill
      需要了解bind和new在一起用的时候的优先级,真正的polyfill是需要考虑到这一点的
    • 写一个数组去重
      自然回答通过哈希记录,存在的就不放入。但是漏掉了一个NaN的情况,被面试官指出来了。因为无论如何NaN做key的话string的NaN与真实的NaN是一致的
    • == 与 === 区别
      其实考察的是==的隐式类型转换,还好之前看过规范也写过一篇文章,还是比较熟悉的。https://segmentfault.com/a/11…
    • isNaN(null)返回什么?为什么?
      一开始想的很简单,直接回答返回false。后来面试官问isNaN不应该是在不是一个数字的时候返回true吗,那null也不是一个数字,为什么会返回false呢。然后意识到事情并没有这么简单,然后按照基础知识推理了一下,isNaN应该会先执行抽象操作toNumber, 而null toNumber就是0,所以是一个数字,故返回false。
    • 对原型链的理解以及继承的实现
    • 实现一个函数,2个参数为两个构造函数,A的实例继承B
    • 如果让你实现一个Promise,要如何去做
      看过几遍Promise/A+规范,简单说了下重点实现构造函数,状态机制,链式机制,解决程序,then方法。
    • 实现 Promise.all
      自己想得有点偏激,想用链式来做,不过在reject的时候会有问题。面试官提出了一个用计数法resolve的方案,简单易懂。
    • 说出几种解决跨域的解决方案
      CORS,jsonp,iframe,proxy
    • 配置CORS是时候注意的点
      除了正常的头配置之外,复杂请求会发一个OPTIONS,可以通过配置类缓存这个OPTIONS请求,而不是每次都发送,其他具体描述见阮神http://www.ruanyifeng.com/blo…
    • cookie session localStorage区别
    • 说说对eventloop的理解
    • 节流与防抖的区别,实现一个防抖
    • 用promise包装一下xhr
    • vue数据绑定的原理
      主要是依赖收集,Object.defineProperty,getter,setter,watcher
    • vue与原先jquery时代的区别
    • vue父子组件如何传递数据
    • vue非父子组件如何传递数据,不用vuex
      event bus
    • 实现一个事件的机制
      简单实现emit on
    • vue v-model实现的原理
    • vue :xxx.sync实现的原理
    • vuex的实现及其原理
      没答出满意的答案,看过部分源码只是为了确认dispatch哪部分是异步的
    • vue-router守卫类别
    • vue-router的实现
    • webpack loader与plugin的区别
    • webpack用过哪些plugin,列举并说明作用
    • 说说cmd amd区别
    • 模块依赖分析以及加载
      看过webpack打包过后的代码,把__webpack__require__的加载机制以及模块缓存讲了一下。再底层一些静态分析建立模块依赖树,从最底层开始加载。这一部分自己答得也不是很好,还需要继续研究。
    • webpack + vue-router异步组件加载的原理
    • 对于具体分包依赖打包过后的代码
      a组件依赖c b组件依赖c 若单独打包了ab作为单js,会不会单独打包c
    • 自己项目的路径及其作用
    • display: none; visibility: hidden; opacity: 1; 区别
    • 外边距合并的问题,如何解决
    • 如何清除浮动
    • 对BFC的理解
    • 移动端适配的解决方案 rem vw vh
    • 300ms点击延迟的起因及其解决办法
    • 对重绘和重排的理解
    • 从接收到html到绘制完整页面的过程
    • 对script标签的理解
      主要是下载可以使同步并发的,而执行必须是按照script标签的位置有序执行的
    • 对前端缓存的理解
      强缓存和协商缓存
    • requestAnimationFrame的理解与如何模拟
    • 对浏览器优化方面有哪些方案与理解
    • 箭头函数与普通函数的区别
    • 对ES7与ES8的了解
      ES7基本就是async await ES8就真的不了解拉

结语

基本还有记忆的就是这些问题,大多数是一些基础问题,有的问题当时回答的并不好,回来也查了资料加深了理解,能跟大牛对话收货就是很多,也已经拿到了满意的offer,希望在找工作的大家加油~祝早日拿到满意的offer~

聊聊一款轻巧的jQuery提示框插件:Tipso

当我们开发项目的时候,页面的一种效果往往可以用多种方式来实现,此时就要考验各位童鞋的技术功底了。比如实现提示tips功能,一些童鞋可能很快想到Tooltip,因为它功能强大,又好用;但是有没有其他的方法也可以更快的实现呢。这就是今天我们要分享的主题:聊聊一款轻巧的jQuery提示框插件:Tipso。

Tipso介绍

tipso是一款非常实用的轻量级响应式jQuery插件。该插件可以和Animate.css配合使用,制作各种显示时的动画效果。还可以通过Ajax来动态加载提示的内容。并且提供了大量参数来控制提示层的显示和行为。

Tipso配置

Tipso 基于 jQuery,提供了很多配置,如:

  • 显示动画持续时间
  • 背景颜色
  • 文本颜色
  • 显示位置(上/右/下/左)
  • 宽度
  • 偏移
  • 加载 ajax 内容
  • 回调函数

相关的配置,我们可以看下如下表格:

属性/方法 类型 默认值 说明
speed 整数 400 动画持续时间
background 字符串 ‘#55b555’ 背景颜色
color 字符串 ‘#ffffff’ 文本颜色
position 字符串 ‘top’ 显示位置
width 整数 200 宽度
delay 整数 200 延迟
offsetX 整数 0 水平偏移
offsetY 整数 0 垂直偏移
content 字符串 null 内容
ajaxContentUrl 字符串 null 异步加载内容
useTitle 布尔值 true 使用 title 属性值作为内容
onBeforeShow 函数 null 执行前的回调函数
onShow 函数 null 显示前的回调函数
onHide 函数 null 显示后的回调函数

里面的配置就不用多说了,相信大家都能看懂,有点模糊的童鞋,可以看后面的实例分析,接下来我们介绍如何应用tipso。

Tipso应用

1、引入文件

  1. <link rel=“stylesheet” href=“css/tipso.css”>
  2. <script src=“js/jquery.js”></script>
  3. <script src=“js/tipso.js”></script>

2、HTML

  1. <span id=“tip” data-tipso=“Web前端之家”>Tipso</span>

或者:

  1. <span id=“tip” title=“内容来自 title 属性”>Tipso</span>

使用属性 data-tipso 的值作为 tip 的内容是时,需要设置 useTitle: false;使用 title 属性作为 tip 的内容则设置 useTitle: true 或省略此参数。

3、JavaScript

  1. $(function() {
  2.     $(‘#tip’).tipso({
  3.         useTitle: false
  4.     });
  5. });

其实在用JS在页面中调用它的时候,方法有很多种,我们可以根据项目的需求进行配置,我在此分享一些:

  1. $(function() {
  2.     // 1
  3.     $(‘#tip1’).tipso({
  4.         useTitle: false
  5.     });
  6.  
  7.     // 2
  8.     $(‘#tip2’).tipso({
  9.         useTitle: false,
  10.         position: ‘left’
  11.     });
  12.  
  13.     // 3
  14.     $(‘#tip3’).tipso({
  15.         useTitle: false,
  16.         background: ‘tomato’
  17.     });
  18.  
  19.     // 4
  20.     $(‘#tip4’).tipso();
  21.  
  22.     // 5
  23.     $(‘#tip5’).tipso({
  24.         useTitle: false
  25.     });
  26.     $(‘#btn5’).on({
  27.         click: function(e) {
  28.             if($(this).text() == ‘显示’) {
  29.                 $(this).text(‘隐藏’);
  30.                 $(‘#tip5’).tipso(‘show’);
  31.             } else {
  32.                 $(this).text(‘显示’);
  33.                 $(‘#tip5’).tipso(‘hide’);
  34.             }
  35.             e.preventDefault();
  36.         }
  37.     });
  38.  
  39.     // 6
  40.     $(‘#tip6’).tipso({
  41.         useTitle: false
  42.     });
  43.     $(‘#btn6’).on(‘click’, function() {
  44.         var $val = $(this).prev().val();
  45.         if($val) {
  46.             $(‘#tip6’).tipso(‘update’, ‘content’, $val);
  47.         }
  48.     });
  49.  
  50.     // 7
  51.     $(‘#tip7’).tipso({
  52.         useTitle: false
  53.     });
  54.  
  55.     // 8
  56.     $(‘#tip8’).tipso({
  57.         useTitle: false,
  58.         onBeforeShow: function() {
  59.             $(‘#status’).html(‘beforeShow’);
  60.         },
  61.         onShow: function() {
  62.             $(‘#status’).html(‘show’);
  63.         },
  64.         onHide: function() {
  65.             $(‘#status’).html(‘hide’);
  66.         }
  67.     });
  68. });

有些童鞋看完,还是很懵逼,不要紧,我们一起来看下DEMO:点击我查看效果

聊到这里,tipso的基础知识就差不多了,接下来需要用一个实例来分析,进一步的学习。

项目进行中,应用插件并不能100%满足我们的需求,有时候需要进行二次开发,此类情况,经常会遇到吧。比如tipso也一样,我拿最近做的一个H5答题游戏的栗子来说明,如下图:

需求需要三种答题模式,我截了三张图:

上面三张图分别代表三种答题模式,里面的虚线就要用到我们的提示插件tipso;我们发现,第一和第二种模式,可以很轻松的应用tipso,第三种的话就会出现问题,比如在移动端,有多个提示,并且需求要求每次点击只能提示一个,但是插件不具备这个功能,需要二次开发插件。

在点击整块灰色区域和虚线文字的时候,提示和结果页面跳转都正常运行。但是这里我遇到了问题,如果二次开发设置了每次只提示一个tips的功能后,发现点击虚线文字,整个灰色区域也会被选中,这样就会跳转到结果页面,这就是所谓的二次点击吧,遇到这种情况,在测试妹子面前肯定是不行的,需要牺牲下需求了,把灰色区域的点击缩短(只点击前面的圆圈和字母)。这里分享下如何实现每次只提示一个tips的代码:

标了背景的就是我添加的代码,很简单吧!

上面只是介绍一个页面多个提示框的处理方法。如果我们需要把tips做成响应式,比如用REM,这时候我们需要改下参数:

其实方法很简单,把宽度和left值改成rem即可,我们可以这样来处理:

搞定,so easy!!!!

其实还有其他的扩展,比如提示框显示的时候,我们可以搞点动画等等,这里就不多介绍了,因为快到下班点了,今天要去踢球,先只介绍这些吧,如果还有不懂的童鞋或者在项目中应用tipso遇到问题的,都可以咨询我们,加QQ群或者关注微信公众号,一起来探讨何学习吧!

总结

Tipso,也许它并不是最好的提示框插件,因为技术永无止境,并且每个童鞋的知识面不一样,加上互联网的发展有目共睹,总有新的东西会出现,更新换代嘛,所以此次分享只是帮助大家在做项目的时候,开发tips提示功能的时候,会多一种选择罢了。

小程序这件事 撸起袖子加油干

写在前面的话:

初次接触小程序,便被它开发的简易与便捷所吸引。总按耐不住对未知的探索欲望,于是乎撸起袖子来干一个吧。附:小程序开发文档

项目介绍

艺龙酒店小程序实践

  • 使用<swiper>标签实现网页轮播图的效果,同时可以内嵌一些跳转美观而不失实用。
  • 首页界面绑定,四个tabbar,点击更换样式并进入不同的查询界面。

<block>中进行wx:for循环得到四个界面的文字和图片,同时为每个tabbar绑定一个id值进行区分,在三元运算符中绑定的id与js中data中创建的currentTab进行比较,值相等则为盒子添加一个active类,在wxss中提前设置active的区别样式,就可以实现点击切换到不同的样式,同时给下面的时间地点选择界面绑定一个与tabbar对应的id,实现上下界面绑定,最后用同样的方法设置下面界面的区别样式。

<view class="swiper-tab">
    <block wx:for="{{tabbars}}" wx:key="index">
      <view class="swiper-tab-item {{currentTab == item.currentTab?'active':''}}" data-currentTab="{{item.currentTab}}" bindtap="clickTab">
        <image src="{{currentTab==item.currentTab?item.hoverimage:item.image}}" />
        <text>{{item.text}}</text>
      </view>
    </block>
  </view>
  • 利用腾讯地图API对位置信息进行操作,通过wx.setStoragewx.getStorage实现数据本地缓存。

点击目的地进入目的地选择界面(参考代码),然后给每一个城市名添加一个bindtap事件,当城市名被点击时将城市名通过setStorage存入本地缓存,然后通过wx.navigateBack跳回(跳转问题坑多多还需要好好理解可参考脱坑)选择界面并在选择界面的data中添加一个city数据通过wx.getStorage得到城市信息并通过this.setData将信息绑定到首页(选择时间界面同理实现不再赘述),在首页点击“我的位置”就会调用wx.getLocation方法获取当地的位置信息并覆盖city的值(使用地图API前要先引入腾讯地图引入地图与使用参考文档)。

//城市选择界面点击城市
cityTap(e) {
      const val = e.currentTarget.dataset.val || '',
        types = e.currentTarget.dataset.types || '',
        Index = e.currentTarget.dataset.index || '',
        that = this;
      let city = this.data.citySel;
      switch (types) {
        case 'locate':
          //定位内容
          city = this.data.locateCity;
          break;
        case 'national':
          //全国
          city = '全国';
          break;
        case 'new':
          //热门城市
          city = val;
          break;
        case 'list':
          //城市列表
          city = val.cityName;
          break;
      }
      if (city) {
        wx.setStorage({
          key: 'city',
          data: city
        })
      } else {
        console.log('还没有获取城市名');
        this.getLocate();
      }
      setTimeout(() => {
        wx.navigateBack({
          delta: 1, // 回退前 delta(默认为1) 页面
        })
      }, 1000);
    },
    //首页获取缓存中城市名
    onShow(e) {
    var that = this;
    wx.getStorage({
      key: 'city',
      success: function(res){
        let city = res.data;
        that.setData({
          city
        })
      },
      fail: function(res) {
        that.getLocation();
      }
    }) ;
  },
  //获取定位
  getLocation: function(e) {
    var that = this;
    wx.getLocation({
      type: 'wgs84', // 默认为 wgs84 返回 gps 坐标,gcj02 返回可用于 wx.openLocation 的坐标
      success: function (res) {
        //2、根据坐标获取当前位置名称,显示在顶部:腾讯地图逆地址解析
        qqmapsdk.reverseGeocoder({
          location: {
            latitude: res.latitude,
            longitude: res.longitude
          },
          success: function (res) {
            // console.log(res)            
            var address = res.result.formatted_addresses.recommend;
           that.setData({
             city:address
           })
          }
        })
      }
    })
  },
  • 点击查询酒店进入酒店详情页并进行搜索

首先在input框上面绑定bindinput事件获取输入值,然后利用地图APIgetSuggestion()方法获得提示的值,再将值进行筛选判断数据的category属性是否为宾馆酒店,如果是则用一个新数组存放酒店列表值(没有酒店信息的API,暂时只想到这个拙略的方法,所以酒店图片都是相同的),最后将获得的酒店信息驱动到酒店列表界面。

//获取输入值
searchInput(e) {
    const searchInput = e.detail.value;
    this.setData({
      searchInput
    })
  },
  //将输入得到的结果显示到界面
  search(e) {
    const searchInput = this.data.searchInput;
    const city = this.data.city;
    var that = this;
    demo.getSuggestion({
      region: city,
      keyword: searchInput + '酒店',
      region_fix: 1,
      plicy: 0,
      success: (res) => {
        wx.setStorage({
          key: 'hotelList',
          data: res.data,
        })
        let hotelArr = []
        for (let i = 0; i < res.data.length; i++) {
          if (res.data[i].category.indexOf('酒店宾馆') != -1) {
            res.data[i].category = res.data[i].category.slice(5);
            res.data[i].address = res.data[i].address.slice(10);
            hotelArr.push(res.data[i])
          }
        }
        that.setData({
          hotelList: hotelArr
        })
      }
    })
  },
  • 筛选列表的制作,多个数据绑定,EasyMock伪造数据

这个筛选列表利用弹性布局,将页面分配好,然后在右边利用scroll-view实现滚动(这里有一个小坑,scroll必须设置高度才能实现滚动条效果),由于数据量比较多,可以通过伪造数据的方法获取代码会看起来简洁很多。
伪造数据地址用了很多次感觉挺方便的,也推荐给大家。

//远程数据请求
onLoad: function (options) {
    wx.request({
      url: 'https://www.easy-mock.com/mock/5b1ca1e041e3435437657ce0/filter/filter#!method=get',
      success: function (res) {
        that.setData({
          filters: res.data.data
        })
      },
      fail: function () {
        console.log('数据请求失败')
      },
    })
    }
  • wx:for大法好,少写了很多页面代码,wx.showModal弹出框,信息提示,完好的用户体验。
  • 绑定手机号以及换绑

点击进入绑定手机号码界面,输入手机号码获得6位随机验证码绑定手机号,再次进入界面时根据wx:ifwx:else进入不同的界面,如果已经绑定手机则提示已绑定且提供换绑功能。

Page({
  data: {
    inputPhone:'',
    identifyCodeInput: 0,
    identifyCause:0,
    msg:'获取验证码',
    time: 60,
    hasBindPhone: false,
    disabled: true
  },
  //获取输入的手机
  teleInput(e) {
    let inputPhone = e.detail.value;
    this.setData({
      inputPhone
    })
  },
  //获取验证码
  getIdentifyCode(e) {
    var that = this;
    const z = /^[1][3,4,5,7,8][0-9]{9}$/;
    let inputPhone = this.data.inputPhone;
    let msg = this.data.mes;
    let time = this.data.time;
    if(z.test(inputPhone)) {
      var num=""; 
      for(var i=0;i<6;i++) 
      { 
        num+=Math.floor(Math.random()*10); 
      } 
      num = Number(num);
          wx.showModal({
            title: '提示',
            content: '验证码为:' + num,
          })   
        that.setData({
          identifyCause: num
        })     
      
      wx.setStorage({
        key: 'IdentifyCode',
        data: num,
      })
      wx.showToast({
        title: '验证码已发送',
        icon: 'success',
        duration: 1000
      })
      setInterval(function() {
        if(time >0) {
          time --;
        that.setData({
          msg: time + '后重新发送'
        })
         if(time === 0){
          that.setData({
            msg: '重新发送',
            time: 60
          })  
        }
        }
      },1000)
    }else {
      wx.showModal({
        title: '提示',
        content: '请输入正确的手机号码',
      })
    }
  },
  //获取验证码输入值
  identifyCodeInput(e) {
    const value = Number(e.detail.value);
    this.setData({
      identifyCodeInput:value,
      disabled: false
    })
  },
  //校验验证码
  check(e) {
    var that = this;
    const identifyCodeInput = this.data.identifyCodeInput;
    const identifyCause = that.data.IdentifyCause;
    const phoneNum = this.data.inputPhone;
    wx.getStorage({
      key: 'IdentifyCode',
      success:function(res) {
        that.setData({
          identifyCause: res.data
        })       
      }
    })
    if(identifyCodeInput === that.data.identifyCause) {
      wx.showToast({
        title: '手机号绑定成功',
        icon: 'success',
        duration: 1000
      })
      this.setData({
        hasBindPhone: true,
      })
      wx.setStorage({
        key: 'phoneNum',
        data: phoneNum,
        
      })
      wx.switchTab({
        url: '../user/user',
      })
    }else {
      wx.showModal({
        title: '提示',
        content: '验证码输入错误,请重新输入',
        success: function(res) {
          
        }
      })
    }
  },
  //检查用户是否绑定手机号
  onLoad(options) {
    wx.getStorage({
      key: 'phoneNum',
      success:(res)=>{
        if(res.data)
        console.log(res)
        this.setData({
          inputPhone:res.data,
          hasBindPhone: true
        })
      },
    })
  },
  //手机号换绑
  changePhone() {
    this.setData({
      hasBindPhone: false,
      inputPhone:''
    })
  }
})

小结

初次习作,肯定有很多写得不好的地方,但还是希望能给大家带来一点点帮助;不足之处希望大家多多谅解与指导。也希望和大家在这个有爱的社区中一起成长共同进步,比心。附上:源码地址

每个程序员都会的 35 个 jQuery 小技巧

收集的35个 jQuery 小技巧/代码片段,可以帮你快速开发.

1. 禁止右键点击

$(document).ready(function(){
    $(document).bind("contextmenu",function(e){
        return false;
    });
});

2. 隐藏搜索文本框文字

Hide when clicked in the search field, the value.(example can be found below in the comment fields)

$(document).ready(function() {
$("input.text1").val("Enter your search text here");
   textFill($('input.text1'));
});

    function textFill(input){ //input focus text function
     var originalvalue = input.val();
     input.focus( function(){
          if( $.trim(input.val()) == originalvalue ){ input.val(''); }
     });
     input.blur( function(){
          if( $.trim(input.val()) == '' ){ input.val(originalvalue); }
     });
}

3. 在新窗口中打开链接

XHTML 1.0 Strict doesn’t allow this attribute in the code, so use this to keep the code valid.

$(document).ready(function() {
   //Example 1: Every link will open in a new window
   $('a[href^="http://"]').attr("target", "_blank");

   //Example 2: Links with the rel="external" attribute will only open in a new window
   $('a[@rel$='external']').click(function(){
      this.target = "_blank";
   });
});
// how to use
<a href="http://www.opensourcehunter.com" rel=external>open link</a>

4. 检测浏览器

注: 在版本jQuery 1.4中,$.support 替换掉了$.browser 变量

$(document).ready(function() {
// Target Firefox 2 and above
if ($.browser.mozilla && $.browser.version >= "1.8" ){
    // do something
}

// Target Safari
if( $.browser.safari ){
    // do something
}

// Target Chrome
if( $.browser.chrome){
    // do something
}

// Target Camino
if( $.browser.camino){
    // do something
}

// Target Opera
if( $.browser.opera){
    // do something
}

// Target IE6 and below
if ($.browser.msie && $.browser.version <= 6 ){
    // do something
}

// Target anything above IE6
if ($.browser.msie && $.browser.version > 6){
    // do something
}
});

5. 预加载图片

This piece of code will prevent the loading of all images, which can be useful if you have a site with lots of images.

$(document).ready(function() {
jQuery.preloadImages = function()
{
  for(var i = 0; i<ARGUMENTS.LENGTH; jQuery(?<img { i++)>").attr("src", arguments[i]);
  }
}
// how to use
$.preloadImages("image1.jpg");
});

6. 页面样式切换

$(document).ready(function() {
    $("a.Styleswitcher").click(function() {
        //swicth the LINK REL attribute with the value in A REL attribute
        $('link[rel=stylesheet]').attr('href' , $(this).attr('rel'));
    });
// how to use
// place this in your header
<LINK rel=stylesheet type=text/css href="default.css">
// the links
<A href="#" rel=default.css>Default Theme</A>
<A href="#" rel=red.css>Red Theme</A>
<A href="#" rel=blue.css>Blue Theme</A>
});

7. 列高度相同

如果使用了两个CSS列,使用此种方式可以是两列的高度相同。

$(document).ready(function() {
function equalHeight(group) {
    tallest = 0;
    group.each(function() {
        thisHeight = $(this).height();
        if(thisHeight > tallest) {
            tallest = thisHeight;
        }
    });
    group.height(tallest);
}
// how to use
$(document).ready(function() {
    equalHeight($(".left"));
    equalHeight($(".right"));
});
});

8. 动态控制页面字体大小

用户可以改变页面字体大小

$(document).ready(function() {
  // Reset the font size(back to default)
  var originalFontSize = $('html').css('font-size');
    $(".resetFont").click(function(){
    $('html').css('font-size', originalFontSize);
  });
  // Increase the font size(bigger font0
  $(".increaseFont").click(function(){
    var currentFontSize = $('html').css('font-size');
    var currentFontSizeNum = parseFloat(currentFontSize, 10);
    var newFontSize = currentFontSizeNum*1.2;
    $('html').css('font-size', newFontSize);
    return false;
  });
  // Decrease the font size(smaller font)
  $(".decreaseFont").click(function(){
    var currentFontSize = $('html').css('font-size');
    var currentFontSizeNum = parseFloat(currentFontSize, 10);
    var newFontSize = currentFontSizeNum*0.8;
    $('html').css('font-size', newFontSize);
    return false;
  });
});

9. 返回页面顶部功能

For a smooth(animated) ride back to the top(or any location).

$(document).ready(function() {
$('a[href*=#]').click(function() {
 if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'')
 && location.hostname == this.hostname) {
   var $target = $(this.hash);
   $target = $target.length && $target
   || $('[name=' + this.hash.slice(1) +']');
   if ($target.length) {
  var targetOffset = $target.offset().top;
  $('html,body')
  .animate({scrollTop: targetOffset}, 900);
    return false;
   }
  }
  });
// how to use
// place this where you want to scroll to
<A name=top></A>
// the link
<A href="#top">go to top</A>
});

10. 获得鼠标指针XY值

Want to know where your mouse cursor is?

$(document).ready(function() {
   $().mousemove(function(e){
     //display the x and y axis values inside the div with the id XY
    $('#XY').html("X Axis : " + e.pageX + " | Y Axis " + e.pageY);
  });
// how to use
<DIV id=XY></DIV>

});

11.返回顶部按钮

你可以利用 animate 和 scrollTop 来实现返回顶部的动画,而不需要使用其他插件。

// Back to top
$('a.top').click(function () {
  $(document.body).animate({scrollTop: 0}, 800);
  return false;
});
<!-- Create an anchor tag -->
<a href="#">Back to top</a>

改变 scrollTop 的值可以调整返回距离顶部的距离,而 animate 的第二个参数是执行返回动作需要的时间(单位:毫秒)。

12.预加载图片

如果你的页面中使用了很多不可见的图片(如:hover 显示),你可能需要预加载它们:

$.preloadImages = function () {
  for (var i = 0; i < arguments.length; i++) {
    $('<img>').attr('src', arguments[i]);
  }
};

$.preloadImages('img/hover1.png', 'img/hover2.png');

13.检查图片是否加载完成

有时候你需要确保图片完成加载完成以便执行后面的操作:

$('img').load(function () {
  console.log('image load successful');
});

你可以把 img 替换为其他的 ID 或者 class 来检查指定图片是否加载完成。

14.自动修改破损图像

如果你碰巧在你的网站上发现了破碎的图像链接,你可以用一个不易被替换的图像来代替它们。添加这个简单的代码可以节省很多麻烦:

$('img').on('error', function () {
  $(this).prop('src', 'img/broken.png');
});

即使你的网站没有破碎的图像链接,添加这段代码也没有任何害处。

15.鼠标悬停(hover)切换 class 属性

假如当用户鼠标悬停在一个可点击的元素上时,你希望改变其效果,下面这段代码可以在其悬停在元素上时添加 class 属性,当用户鼠标离开时,则自动取消该 class 属性:

$('.btn').hover(function () {
  $(this).addClass('hover');
  }, function () {
    $(this).removeClass('hover');
  });

你只需要添加必要的CSS代码即可。如果你想要更简洁的代码,可以使用 toggleClass 方法:

$('.btn').hover(function () { 
  $(this).toggleClass('hover'); 
});

注:直接使用CSS实现该效果可能是更好的解决方案,但你仍然有必要知道该方法。

16.禁用 input 字段

有时你可能需要禁用表单的 submit 按钮或者某个 input 字段,直到用户执行了某些操作(例如,检查“已阅读条款”复选框)。可以添加 disabled 属性,直到你想启用它时:

$('input[type="submit"]').prop('disabled', true);

你要做的就是执行 removeAttr 方法,并把要移除的属性作为参数传入:

$('input[type="submit"]').removeAttr('disabled');

17.阻止链接加载

有时你不希望链接到某个页面或者重新加载它,你可能希望它来做一些其他事情或者触发一些其他脚本,你可以这么做:

$('a.no-link').click(function (e) {
  e.preventDefault();
});

18.切换 fade/slide

fade 和 slide 是我们在 jQuery 中经常使用的动画效果,它们可以使元素显示效果更好。但是如果你希望元素显示时使用第一种效果,而消失时使用第二种效果,则可以这么做:

// Fade
$('.btn').click(function () {
  $('.element').fadeToggle('slow');
});
// Toggle
$('.btn').click(function () {
  $('.element').slideToggle('slow');
});

19.简单的手风琴效果

这是一个实现手风琴效果快速简单的方法:

// Close all panels
$('#accordion').find('.content').hide();
// Accordion
$('#accordion').find('.accordion-header').click(function () {
  var next = $(this).next();
  next.slideToggle('fast');
  $('.content').not(next).slideUp('fast');
  return false;
});

20.让两个 DIV 高度相同

有时你需要让两个 div 高度相同,而不管它们里面的内容多少。可以使用下面的代码片段:

var $columns = $('.column');
var height = 0;
$columns.each(function () {
  if ($(this).height() > height) {
    height = $(this).height();
  }
});
$columns.height(height);

这段代码会循环一组元素,并设置它们的高度为元素中的最大高。

21. 验证元素是否为空

This will allow you to check if an element is empty.

$(document).ready(function() {
  if ($('#id').html()) {
   // do something
   }
});

22. 替换元素

$(document).ready(function() {
   $('#id').replaceWith('
<DIV>I have been replaced</DIV>

');
});

23. jQuery延时加载功能

$(document).ready(function() {
   window.setTimeout(function() {
     // do something
   }, 1000);
});

24. 移除单词功能

$(document).ready(function() {
   var el = $('#id');
   el.html(el.html().replace(/word/ig, ""));
});

25. 验证元素是否存在于jquery对象集合中

$(document).ready(function() {
   if ($('#id').length) {
  // do something
  }
});

26. 使整个DIV可点击

$(document).ready(function() {
    $("div").click(function(){
      //get the url from href attribute and launch the url
      window.location=$(this).find("a").attr("href"); return false;
    });
// how to use
<DIV><A href="index.html">home</A></DIV>

});

27. ID与Class之间转换

当改变Window大小时,在ID与Class之间切换

$(document).ready(function() {
   function checkWindowSize() {
    if ( $(window).width() > 1200 ) {
        $('body').addClass('large');
    }
    else {
        $('body').removeClass('large');
    }
   }
$(window).resize(checkWindowSize);
});

28. 克隆对象

$(document).ready(function() {
   var cloned = $('#id').clone();
// how to use
<DIV id=id></DIV>

});

29. 使元素居屏幕中间位置

$(document).ready(function() {
  jQuery.fn.center = function () {
      this.css("position","absolute");
      this.css("top", ( $(window).height() - this.height() ) / 2+$(window).scrollTop() + "px");
      this.css("left", ( $(window).width() - this.width() ) / 2+$(window).scrollLeft() + "px");
      return this;
  }
  $("#id").center();
});

30. 写自己的选择器

$(document).ready(function() {
   $.extend($.expr[':'], {
       moreThen1000px: function(a) {
           return $(a).width() > 1000;
      }
   });
  $('.box:moreThen1000px').click(function() {
      // creating a simple js alert box
      alert('The element that you have clicked is over 1000 pixels wide');
  });
});

31. 统计元素个数

$(document).ready(function() {
   $("p").size();
});

32. 使用自己的 Bullets

$(document).ready(function() {
   $("ul").addClass("Replaced");
   $("ul > li").prepend("‒ ");
 // how to use
 ul.Replaced { list-style : none; }
});

33. 引用Google主机上的Jquery类库

//Example 1
<SCRIPT src="http://www.google.com/jsapi"></SCRIPT>
<SCRIPT type=text/javascript>
google.load("jquery", "1.2.6");
google.setOnLoadCallback(function() {
    // do something
});
</SCRIPT><SCRIPT type=text/javascript src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></SCRIPT>

 // Example 2:(the best and fastest way)
<SCRIPT type=text/javascript src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></SCRIPT>

34. 禁用Jquery(动画)效果

$(document).ready(function() {
    jQuery.fx.off = true;
});

35. 与其他Javascript类库冲突解决方案

$(document).ready(function() {
   var $jq = jQuery.noConflict();
   $jq('#id').show();
});

HTML5 手势检测原理和实现

前言

随着 Hybrid 应用的丰富,HTML5 工程师们已经不满足于把桌面端体验简单移植到移动端,他们觊觎移动原生应用人性化的操作体验,特别是原生应用与生俱来的丰富的手势系统。HTML5 没有提供开箱即用的手势系统,但是提供了更底层一些的对 touch 事件的监听。基于此,我们可以做出自己的手势库。

手势

常用的 HTML5 手势可以分为两类,单点手势和两点手势。单点手势有 tap(单击),double tap(双击),long tap(长按),swipe(挥),move(移动)。两点手势有 pinch(缩放),rotate(旋转)。

接下来我们实现一个检测这些手势的 javaScript 库,并利用这个手势库做出炫酷的交互效果。

移动

关于移动手势检测我们这里不再赘述。总结一下就是在每次touchmove事件发生时,把两个位移点之间的坐标位置相减,就可以了。

单击(tap)

手势检测的关键是用 touchstart,touchmove,touchend 三个事件对手势进行分解。

那么怎么分解单击事件呢?

  1. 在 touchstart 发生时进入单击检测,只有一个接触点。因为单击事件限制为一个手指的动作。
  2. 没有发生 touchmove 事件或者 touchmove 在一个很小的范围(如下图)。限制 touchmove 在一个很小范围,是为了给用户一定的冗余空间,因为不能保证用户手指在接触屏幕的时候不发生轻微的位移。

3.touchend 发生在 touchstart后的很短时间内(如下图)。这个时间段的阈值是毫秒级,用来限制手指和屏幕接触的时间。因为单击事件从开始到结束是很快的。

有了上面的流程,就可以开始实现 tap 事件监测了。

_getTime() {

  return new Date().getTime(); 

}

_onTouchStart(e) {

    //记录touch开始的位置

    this.startX = e.touches[0].pageX;

    this.startY = e.touches[0].pageY;

    if(e.touches.length > 1) {

      //多点监测

      ...

    }else {

      //记录touch开始的时间

      this.startTime = this._getTime();

    }

 }

_onTouchMove(e) {

  ...

  //记录手指移动的位置

  this.moveX = e.touches[0].pageX;

  this.moveY = e.touches[0].pageY;

  ...

}

_onTouchEnd(e) {

  let timestamp = this._getTime();

  if(this.moveX !== null && Math.abs(this.moveX - this.startX) > 10 ||

    this.moveY !== null && Math.abs(this.moveY - this.startY) > 10) {

      ...

  }else {

    //手指移动的位移要小于10像素并且手指和屏幕的接触时间要短语500毫秒

    if(timestamp - this.startTime < 500) {

      this._emitEvent('onTap')

    }

  }

}

双击(double tap)

和单击一样,双击事件也需要我们对手势进行量化分解。

  1. 双击事件是一个手指的行为。所以在 touchstart 时,我们要判断此时屏幕有几个接触点。
  2. 双击事件中包含两次独立的单击行为。理想情况下,这两次点击应该落在屏幕上的同一个点上。为了给用户一定的冗余空间,将两次点击的坐标点距离限制在10个像素以内。
  3. 双击事件本质是两次快速的单击。也即是说,两次点击的间隔时间很短。通过一定的测试量化后,我们把两次单击的时间间隔设为300毫秒。

注意双击事件中我们检测了相邻两个 touchstart 事件的位移和时间间隔。

_onTouchStart(e) {

  if(e.touches.length > 1) {

  ...

  } else {

    if(this.previousTouchPoint) {

      //两次相邻的touchstart之间距离要小于10,同时时间间隔小于300ms

      if( Math.abs(this.startX -this.previousTouchPoint.startX) < 10  &&

          Math.abs(this.startY - this.previousTouchPoint.startY) < 10 && 

          Math.abs(this.startTime - this.previousTouchTime) < 300) {

            this._emitEvent('onDoubleTap');

          }

    }

    //保存上一次touchstart的时间和位置信息

    this.previousTouchTime = this.startTime;

    this.previousTouchPoint = {

        startX : this.startX,

        startY : this.startY

     };

  }

}

长按(long press)

长按应该是最容易分解的手势。我们可以这样分解:在 touchstart 发生后的很长一段时间内,如果没有发生 touchmove 或者 touchend 事件,那么就触发长按手势。

  1. 长按是一个手指的行为,需要检测屏幕上是否只有一个接触点。
  2. 如果手指在空间上发生了移动,那么长按事件取消。
  3. 如果手指在屏幕上停留的时间超过800ms,那么触发长按手势。
  4. 如果手指在屏幕上停留的时间小于800ms,也即 touchend 在 touchstart 发生后的800ms内触发,那么长按事件取消。
_onTouchStart(e) {

  clearTimeout(this.longPressTimeout);

  if(e.touches.length > 1) {

  }else {

    this.longPressTimeout = setTimeout(()=>{

      this._emitEvent('onLongPress');

    });

  }

}

_onTouchMove(e) {

  ...

  clearTimeout(this.longPressTimeout);

  ...

}

_onTouchEnd(e) {

  ...

  clearTimeout(this.longPressTimeout);

  ...

}

缩放(pinch)

缩放是一个非常有趣的手势,还记得第一代iPhone双指缩放图片给你带来的震撼吗?虽然如此,缩放手势的检测却相对简单。

  1. 缩放是两个手指的行为,需要检测屏幕上是否有两个接触点。
  2. 缩放比例的量化,是通过两次缩放行为之间的距离的比值得到,如下图。

所以缩放的核心是获取两个接触点之间的直线距离。

//勾股定理

_getDistance(xLen,yLen) {
   return Math.sqrt(xLen * xLen + yLen * yLen);
  }

这里的xLen是两个接触点x坐标差的绝对值,yLen相应的就是y坐标差的绝对值。

_onTouchStart(e) {

  if(e.touches.length > 1) {

    let point1 = e.touches[0];

    let point2 = e.touches[1];

    let xLen = Math.abs(point2.pageX - point1.pageX);

    let yLen = Math.abs(point2.pageY - point1.pageY);

    this.touchDistance = this._getDistance(xLen, yLen);

  } else {

    ...

  }

}

在_onTouchStart函数中获取并且保存 touchstart 发生时两个接触点之间的距离。

_onTouchMove(e) {

  if(e.touches.length > 1) {

      let xLen = Math.abs(e.touches[0].pageX - e.touches[1].pageX);

      let yLen = Math.abs(e.touches[1].pageY - e.touches[1].pageY);

      let touchDistance = this._getDistance(xLen,yLen);

      if(this.touchDistance) {

        let pinchScale = touchDistance / this.touchDistance;

          this._emitEvent('onPinch',{scale:pinchScale - this.previousPinchScale});

          this.previousPinchScale = pinchScale;

      }

  }else {

    ...

  }

}

旋转(rotate)

旋转手势需要检测两个比较重要的值,一是旋转的角度,二是旋转的方向(顺时针或逆时针)。

其中旋转角度和方向的计算需要通过向量的计算来获取,本文不再展开。

首先,需要获取向量的旋转方向和角度。

//这两个方法属于向量计算,具体原理请阅读本文最后的参考文献

  _getRotateDirection(vector1,vector2) {

    return vector1.x * vector2.y - vector2.x * vector1.y;

  }  

  _getRotateAngle(vector1,vector2) {

    let direction = this._getRotateDirection(vector1,vector2);

    direction = direction > 0 ? -1 : 1;

    let len1 = this._getDistance(vector1.x,vector1.y);

    let len2 = this._getDistance(vector2.x,vector2.y);

    let mr = len1 * len2;

    if(mr === 0) return 0;

    let dot = vector1.x * vector2.x + vector1.y * vector2.y;

    let r = dot / mr;

    if(r > 1) r = 1;

    if(r < -1) r = -1;

    return Math.acos(r) * direction * 180 / Math.PI;

  }

然后,我们在手指发生移动时,调用获取旋转方向和角度的方法。

_onTouchStart(e) {

  ...  

  if(e.touches.length > 1) {

    this.touchVector = {

       x: point2.pageX - this.startX,

       y: point2.pageY - this.startY

     };

  }

  ...

}

_onTouchMove(e) {

  ...

  if(this.touchVector) {

        let vector = {

          x: e.touches[1].pageX - e.touches[0].pageX,

          y: e.touches[1].pageY - e.touches[0].pageY

        };

        let angle = this._getRotateAngle(vector,this.touchVector);

        this._emitEvent('onRotate',{

          angle

        });

        this.touchVector.x = vector.x;

        this.touchVector.y = vector.y;

      }

  ...

}

实战

好了,我们的手势系统到这里就完成了。接下来要在实战中检验这套系统是否可靠,做一个简单的图片浏览器,支持图片缩放,旋转,移动,长按。

首先,做好DOM规划,和“之前”一样,我们的事件监听机制并不直接作用在图片上,而是作用在图片的父元素上。

然后,可以开始使用上面的手势检测系统了。

render() {

    return (

      <Gestures onPinch={this.onPinch} onMove={this.onMove} onRotate={this.onRotate} onDoubleTap={this.onDoubleTap} onLongPress={this.onLongPress}>

        <div className="wrapper" >

          ![](http://upload-images.jianshu.io/upload_images/2362670-f8b44d4b9101e8d6.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

        </div>

      </Gestures>

    );

  }

由于我们的手势系统检测的增量,因此不能直接把增量应用在对象上,而是需要把这些增量累加。以旋转为例:

onRotate(event) {

    //对增量进行累加

    this.angle += event.angle

    this.setState({

      angle:this.angle

    });

  }

至此,我们的手势检测就完成了。

源码:https://github.com/eeandrew/gestures

在线Demo: http://eeandrew.github.io/demos/gestures/index.html

高效的jQuery代码编写技巧总结

最近写了很多的js,虽然效果都实现了,但是总感觉自己写的js在性能上还能有很大的提升。本文我计划总结一些网上找的和我本人的一些建议,来提升你的jQuery和javascript代码。好的代码会带来速度的提升。快速渲染和响应意味着更好的用户体验。谨以此文来提醒自己。

其次在脑子里牢牢记住jQuery就是javascript。这意味着我们应该采取相同的编码惯例,风格指南和最佳实践。

当你准备使用jQuery,我强烈建议你遵循下面这些指南:

缓存变量

DOM遍历是昂贵的,所以尽量将会重用的元素缓存。

// 糟糕

h = $('#element').height();
$('#element').css('height',h-20);

// 建议

$element = $('#element');
h = $element.height();
$element.css('height',h-20);

避免全局变量

jQuery与javascript一样,一般来说,最好确保你的变量在函数作用域内。

// 糟糕

$element = $('#element');
h = $element.height();
$element.css('height',h-20);

// 建议

var $element = $('#element');
var h = $element.height();
$element.css('height',h-20);

使用匈牙利命名法

在变量前加$前缀,便于识别出jQuery对象。

// 糟糕

var first = $('#first');
var second = $('#second');
var value = $first.val();

// 建议 - 在jQuery对象前加$前缀

var $first = $('#first');
var $second = $('#second'),
var value = $first.val();

使用 Var 链(单 Var 模式)

将多条var语句合并为一条语句,我建议将未赋值的变量放到后面。

var
  $first = $('#first'),
  $second = $('#second'),
  value = $first.val(),
  k = 3,
  cookiestring = 'SOMECOOKIESPLEASE',
  i,
  j,
  myArray = {};

请使用’On’

在新版jQuery中,更短的 on(“click”) 用来取代类似 click() 这样的函数。在之前的版本中 on() 就是 bind()。自从jQuery 1.7版本后,on() 附加事件处理程序的首选方法。然而,出于一致性考虑,你可以简单的全部使用 on()方法。

// 糟糕

$first.click(function(){
    $first.css('border','1px solid red');
    $first.css('color','blue');
});

$first.hover(function(){
    $first.css('border','1px solid red');
})

// 建议
$first.on('click',function(){
    $first.css('border','1px solid red');
    $first.css('color','blue');
})

$first.on('hover',function(){
    $first.css('border','1px solid red');
})

精简javascript

一般来说,最好尽可能合并函数。

// 糟糕

$first.click(function(){
    $first.css('border','1px solid red');
    $first.css('color','blue');
});

// 建议

$first.on('click',function(){
    $first.css({
        'border':'1px solid red',
        'color':'blue'
    });
});

链式操作

jQuery实现方法的链式操作是非常容易的。下面利用这一点。

// 糟糕

$second.html(value);
$second.on('click',function(){
    alert('hello everybody');
});
$second.fadeIn('slow');
$second.animate({height:'120px'},500);

// 建议

$second.html(value);
$second.on('click',function(){
    alert('hello everybody');
}).fadeIn('slow').animate({height:'120px'},500);

维持代码的可读性

伴随着精简代码和使用链式的同时,可能带来代码的难以阅读。添加缩紧和换行能起到很好的效果。

// 糟糕

$second.html(value);
$second.on('click',function(){
    alert('hello everybody');
}).fadeIn('slow').animate({height:'120px'},500);

// 建议

$second.html(value);
$second
    .on('click',function(){ alert('hello everybody');})
    .fadeIn('slow')
    .animate({height:'120px'},500);

选择短路求值

短路求值是一个从左到右求值的表达式,用 &&(逻辑与)或 ||(逻辑或)操作符。

// 糟糕

function initVar($myVar) {
    if(!$myVar) {
        $myVar = $('#selector');
    }
}

// 建议

function initVar($myVar) {
    $myVar = $myVar || $('#selector');
}

选择捷径

精简代码的其中一种方式是利用编码捷径。

// 糟糕

if(collection.length > 0){..}

// 建议

if(collection.length){..}

繁重的操作中分离元素

如果你打算对DOM元素做大量操作(连续设置多个属性或css样式),建议首先分离元素然后在添加。

// 糟糕

var
    $container = $("#container"),
    $containerLi = $("#container li"),
    $element = null;

$element = $containerLi.first();
//... 许多复杂的操作

// better

var
    $container = $("#container"),
    $containerLi = $container.find("li"),
    $element = null;

$element = $containerLi.first().detach();
//... 许多复杂的操作

$container.append($element);

熟记技巧

你可能对使用jQuery中的方法缺少经验,一定要查看的文档,可能会有一个更好或更快的方法来使用它。

// 糟糕

$('#id').data(key,value);

// 建议 (高效)

$.data('#id',key,value);

使用子查询缓存的父元素

正如前面所提到的,DOM遍历是一项昂贵的操作。典型做法是缓存父元素并在选择子元素时重用这些缓存元素。

// 糟糕

var
    $container = $('#container'),
    $containerLi = $('#container li'),
    $containerLiSpan = $('#container li span');

// 建议 (高效)

var
    $container = $('#container '),
    $containerLi = $container.find('li'),
    $containerLiSpan= $containerLi.find('span');

避免通用选择符

将通用选择符放到后代选择符中,性能非常糟糕。

// 糟糕

$('.container > *');

// 建议

$('.container').children();

避免隐式通用选择符

通用选择符有时是隐式的,不容易发现。

// 糟糕

$('.someclass :radio');

// 建议

$('.someclass input:radio');

优化选择符

例如,Id选择符应该是唯一的,所以没有必要添加额外的选择符。

// 糟糕

$('div#myid');
$('div#footer a.myLink');

// 建议
$('#myid');
$('#footer .myLink');

避免多个ID选择符

在此强调,ID 选择符应该是唯一的,不需要添加额外的选择符,更不需要多个后代ID选择符。

// 糟糕

$('#outer #inner');

// 建议

$('#inner');

坚持最新版本

新版本通常更好:更轻量级,更高效。显然,你需要考虑你要支持的代码的兼容性。例如,2.0版本不支持ie 6/7/8。

摒弃弃用方法

关注每个新版本的废弃方法是非常重要的并尽量避免使用这些方法。

// 糟糕 - live 已经废弃

$('#stuff').live('click', function() {
  console.log('hooray');
});

// 建议
$('#stuff').on('click', function() {
  console.log('hooray');
});
// 注:此处可能不当,应为live能实现实时绑定,delegate或许更合适

利用CDN

谷歌的CND能保证选择离用户最近的缓存并迅速响应。(使用谷歌CND请自行搜索地址,此处地址以不能使用,推荐jquery官网提供的CDN)。

必要时组合jQuery和javascript原生代码

如上所述,jQuery就是javascript,这意味着用jQuery能做的事情,同样可以用原生代码来做。原生代码(或vanilla)的可读性和可维护性可能不如jQuery,而且代码更长。但也意味着更高效(通常更接近底层代码可读性越差,性能越高,例如:汇编,当然需要更强大的人才可以)。牢记没有任何框架能比原生代码更小,更轻,更高效(注:测试链接已失效,可上网搜索测试代码)。

鉴于vanilla 和 jQuery之间的性能差异,我强烈建议吸收两人的精华,使用(可能的话)和jQuery等价的原生代码。

最后忠告

最后,我记录这篇文章的目的是提高jQuery的性能和其他一些好的建议。如果你想深入的研究对这个话题你会发现很多乐趣。记住,jQuery并非不可或缺,仅是一种选择。思考为什么要使用它。DOM操作?ajax?模版?css动画?还是选择符引擎?或许javascript微型框架或jQuery的定制版是更好的选择。

虽然都是陈词滥调,但是我发现还不能很好得做到上述所有,记录下来希望自己能够全部做到。

手机端页面在项目中遇到的一些问题及解决办法

1.解决页面使用overflow: scroll在iOS上滑动卡顿的问题?

首先你可能会给页面的html和body增加了height: 100%, 然后就可能造成IOS上页面滑动的卡顿问题。解决方案是:
(1) 看是否能把body和html的height: 100%去除掉。
(2) 在滚动的容器中增加:-webkit-overflow-scrolling: touch或者给body增加:body {overflow-x: hidden}。

2.ios页面橡皮弹回效果遮挡页面选项卡?

(1) 有时body和html的height: 100%去除掉问题可能就没有了。
(2) 到达临界值的时候在阻止事件默认行为

var startY,endY;
//记录手指触摸的起点坐标
$('body').on('touchstart',function (e) {
     startY = e.touches[0].pageY;
});
$('body').on('touchmove',function (e) {
     endY = e.touches[0].pageY;  //记录手指触摸的移动中的坐标
     //手指下滑,页面到达顶端不能继续下滑
     if(endY>startY&& $(window).scrollTop()<=0){
         e.preventDefault();
     }
   //手指上滑,页面到达底部能继续上滑
     if(endY<startY&& $(window).scrollTop()+ 
         $(window).height()>=$('body')[0].scrollHeight){
         e.preventDefault();
     }
})

有时也会碰见弹窗出来后两个层的橡皮筋效果出现问题,我们可以在弹出弹出时给底层页面加上一个类名,类名禁止页面滑动这样下层的橡皮筋效果就会被禁止,就不会影响弹窗层。

3.IOS机型margin属性无效问题?

(1) 设置html body的高度为百分比时,margin-bottom在safari里失效
(2) 直接padding代替margin

4.Ios绑定点击事件不执行?

(1)添加样式cursor :pointer。点击后消除背景闪一下的css:-webkit-tap-highlight-color:transparent;

5.Ios键盘换行变为搜索?

  • 首先,input 要放在 form里面。
  • 这时 “换行” 已经变成 “前往”。
  • 如果想变成 “搜索”,input 设置 type=”search”。

6.Jq对a标签点击事件不生效?

出现这种情况的原因不明,有的朋友解释:我们平时都是点击的A标签中的文字了。 所以要想用JS模拟点击A标签事件,就得先往A标签中的文字添加能被JS捕获的元素,然后再用JS模拟点击该元素即可。但是我觉得不合理,虽然找不到原因但是解决办法还是有的。
(1)document.getElementById(“abc “).click();
(2)$(“#abc “)[0].click();

7.有时因为服务器或者别的原因导致页面上的图片没有找到?

这是我们想需要用一个本地的图片代替没有找的的图片

<script type="text/javascript"> 
function nofind(){ 
var img=event.srcElement; 
img.src="images/logoError.png"; 
img.onerror=null; 控制不要一直跳动 
} 
</script> 

<img src="images/logo.png" onerror="nofind();" />

8.transform属性影响position:fixed?

(1)规范中有规定:如果元素的transform值不为none,则该元素会生成包含块和层叠上下文。CSS Transforms Module Level 1不只在手机上,电脑上也一样。除了fixed元素会受影响之外,z-index(层叠上下文)值也会受影响。绝对定位元素等和包含块有关的属性都会受到影响。当然如果transform元素的display值为inline时又会有所不同。最简单的解决方法就是transform元素内部不能有absolute、fixed元素.

9.ios对position: fixed不太友好,有时我们需要加点处理?

在安卓上面,点击页面底部的输入框,软键盘弹出,页面移动上移。
而ios上面,点击页面底部输入框,软键盘弹出,输入框看不到了。。。查资料说什么的都有,iscroll,jquery-moblie,absolute,fixe,static都非常复杂,要改很多。。。
让他弹出时让滚动条在最低部

var u = navigator.userAgent, app = navigator.appVersion;
var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
if (isiOS) {
    $('textarea').focus(function () {
        window.setTimeout('scrollBottom()', 500);
    });
}
function scrollBottom() {
    window.scrollTo(0, $('body').height());
}

10.jq validate插件验证问题?

(1)所以的input必须有name不然会出错

11.有时手机会出现断网的情况,我没可能会对断网的情况做一些处理?

(1)navigator.onLine可判断是否是脱机状态.

12.判断对象的长度?

(1)用Object.keys,Object.keys方法返回的是一个数组,数组里面装的是对象的属性。

var person = {
    "name" : "zhangshan",
    "sex" : "man",
    "age" : "50",
    "height" : "180",
    "phone" : "1xxxxxxxxxx",
    "email" : "xxxxxxxxx@xxx.com"
};
var arr = Object.keys(person);
console.log(arr.length);

(2)Object.getOwnPropertyNames(obj).length

13.上一题我们用到了Object.keys与Object.getOwnPropertyNames他们的区别?

Object.keys定义:返回一个对象可枚举属性的字符串数组;
Object.getOwnPropertyNames定义:返回一个对象可枚举、不可枚举属性的名称;
属性的可枚举性、不可枚举性:定义:可枚举属性是指那些内部 “可枚举” 标志设置为 true 的属性,对于通过直接的赋值和属性初始化的属性,该标识值默认为即为 true,对于通过 Object.defineProperty 等定义的属性,该标识值默认为 false。

var obj = { "prop1": "v1" };
Object.defineProperty(obj, "prop2", { value: "v2", enumerable: false });
console.log(Object.keys(obj).length);           //output:1
console.log(Object.getOwnPropertyNames(obj).length);    //output:2

console.log(Object.keys(obj));           //output:Array[1] => [0: "prop1"]
console.log(Object.getOwnPropertyNames(obj));    //output:Array[2] => [0: "prop1", 1: "prop2"]


综合实例:

var obj = { "prop1": "v1" };
Object.defineProperty(obj, "prop2", { value: "v2", enumerable: false});

console.log(obj.hasOwnProperty("prop1")); //output: true
console.log(obj.hasOwnProperty("prop2")); //output: true

console.log(obj.propertyIsEnumerable("prop1")); //output: true
console.log(obj.propertyIsEnumerable("prop2")); //output: false

console.log('prop1' in obj);    //output: true
console.log('prop2' in obj);    //output: true

for (var item in obj) {
    console.log(item);
}
//output:prop1

for (var item in Object.getOwnPropertyNames(obj)) {
    console.log(Object.getOwnPropertyNames(obj)[item]);
}
//ouput:[prop1,prop2]

14.移动开发不同手机弹出数字键盘问题?

(1)type=”tel”
iOS和Android的键盘表现都差不多
(2)type=”number”
优点是Android下实现的一个真正的数字键盘
缺点一:iOS下不是九宫格键盘,输入不方便
缺点二:旧版Android(包括微信所用的X5内核)在输入框后面会有超级鸡肋的小尾巴,好在Android 4.4.4以后给去掉了。
不过对于缺点二,我们可以用webkit私有的伪元素给fix掉:

input[type=number]::-webkit-inner-spin-button,  
input[type=number]::-webkit-outer-spin-button { 
        -webkit-appearance: none; 
        appearance: none; 
        margin: 0; 
}

(3)pattern属性
pattern用于验证表单输入的内容,通常HTML5的type属性,比如email、tel、number、data类、url等,已经自带了简单的数据格式验证功能了,加上pattern后,前端部分的验证更加简单高效了。
显而易见,pattern的属性值要用正则表达式。
实例 简单的数字验证
数字的验证有两个:
<input type=”number” pattern=”d”>
<input type=”number” pattern=”[0-9]*”>

15.input[number]类型输入非数字字符

js获取的值是空;比如-12,+123等

16.Javascript:history.go()和history.back()的用法与区别?

简单的说就是:go(-1):返回上一页,原页面表单中的内容会丢失;back():返回上一页,原页表表单中的内容会保留。history.go(-1):后退+刷新history.back():后退
之所以注意到这个区别,是因为不同的浏览器后退行为也是有区别的,而区别就跟javascript:history.go()和history.back()的区别类似。
Chrome和ff浏览器后退页面,会刷新后退的页面,若有数据请求也会提交数据申请。类似于history.go(-1);
而safari(包括桌面版和ipad版)的后退按钮则不会刷新页面,也不会提交数据申请。类似于javascript:history.back();

17.Meta基础知识:

<meta name="viewport"content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
// width    设置viewport宽度,为一个正整数,或字符串‘device-width’
// height   设置viewport高度,一般设置了宽度,会自动解析出高度,可以不用设置
// initial-scale    默认缩放比例,为一个数字,可以带小数
// minimum-scale    允许用户最小缩放比例,为一个数字,可以带小数
// maximum-scale    允许用户最大缩放比例,为一个数字,可以带小数
// user-scalable    是否允许手动缩放 
空白页基本meta标签
<!-- 设置缩放 -->
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, minimal-ui" />
<!-- 可隐藏地址栏,仅针对IOS的Safari(注:IOS7.0版本以后,safari上已看不到效果) -->
<meta name="apple-mobile-web-app-capable" content="yes" />
<!-- 仅针对IOS的Safari顶端状态条的样式(可选default/black/black-translucent ) -->
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<!-- IOS中禁用将数字识别为电话号码/忽略Android平台中对邮箱地址的识别 -->
<meta name="format-detection"content="telephone=no, email=no" />
其他meta标签
<!-- 启用360浏览器的极速模式(webkit) -->
<meta name="renderer" content="webkit">
<!-- 避免IE使用兼容模式 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 针对手持设备优化,主要是针对一些老的不识别viewport的浏览器,比如黑莓 -->
<meta name="HandheldFriendly" content="true">
<!-- 微软的老式浏览器 -->
<meta name="MobileOptimized" content="320">
<!-- uc强制竖屏 -->
<meta name="screen-orientation" content="portrait">
<!-- QQ强制竖屏 -->
<meta name="x5-orientation" content="portrait">
<!-- UC强制全屏 -->
<meta name="full-screen" content="yes">
<!-- QQ强制全屏 -->
<meta name="x5-fullscreen" content="true">
<!-- UC应用模式 -->
<meta name="browsermode" content="application">
<!-- QQ应用模式 -->
<meta name="x5-page-mode" content="app">
<!-- windows phone 点击无高光 -->
<meta name="msapplication-tap-highlight" content="no">

18.移动端如何定义字体font-family?
@ ————————————–中文字体的英文名称
@ 宋体 SimSun
@ 黑体 SimHei
@ 微信雅黑 Microsoft Yahei
@ 微软正黑体 Microsoft JhengHei
@ 新宋体 NSimSun
@ 新细明体 MingLiU
@ 细明体 MingLiU
@ 标楷体 DFKai-SB
@ 仿宋 FangSong
@ 楷体 KaiTi
@ 仿宋_GB2312 FangSong_GB2312
@ 楷体_GB2312 KaiTi_GB2312
@
@ 说明:中文字体多数使用宋体、雅黑,英文用Helvetica

body { font-family: Microsoft Yahei,SimSun,Helvetica; }

19.打电话发短信写邮件怎么实现?

// 一、打电话
<a href="tel:0755-10086">打电话给:0755-10086</a>

//  二、发短信,winphone系统无效
<a href="sms:10086">发短信给: 10086</a>

// 三、写邮件
<a href="mailto:863139978@qq.com">点击我发邮件</a>
//2.收件地址后添加?cc=开头,可添加抄送地址(Android存在兼容问题)
<a href="mailto:863139978@qq.com?cc=zhangqian0406@yeah.net">点击我发邮件</a>
//3.跟着抄送地址后,写上&bcc=,可添加密件抄送地址(Android存在兼容问题)
<a href="mailto:863139978@qq.com?cc=zhangqian0406@yeah.net&bcc=384900096@qq.com">点击我发邮件</a>
//4.包含多个收件人、抄送、密件抄送人,用分号(;)隔开多个邮件人的地址
<a href="mailto:863139978@qq.com;384900096@qq.com">点击我发邮件</a>
//5.包含主题,用?subject=
<a href="mailto:863139978@qq.com?subject=邮件主题">点击我发邮件</a>
//6.包含内容,用?body=;如内容包含文本,使用%0A给文本换行 
<a href="mailto:863139978@qq.com?body=邮件主题内容%0A腾讯诚信%0A期待您的到来">点击我发邮件</a>
//7.内容包含链接,含http(s)://等的文本自动转化为链接
<a href="mailto:863139978@qq.com?body=http://www.baidu.com">点击我发邮件</a>
//8.内容包含图片(PC不支持)
<a href="mailto:863139978@qq.com?body=<img src='images/1.jpg' />">点击我发邮件</a>
//9.完整示例
<a href="mailto:863139978@qq.com;384900096@qq.com?cc=zhangqian0406@yeah.net&bcc=993233461@qq.com&subject=[邮件主题]&body=腾讯诚邀您参与%0A%0Ahttp://www.baidu.com%0A%0A<img src='images/1.jpg' />">点击我发邮件</a>

20.移动端touch事件(区分webkit和winphone)?

// 以下支持webkit
touchstart——当手指触碰屏幕时候发生。不管当前有多少只手指
touchmove——当手指在屏幕上滑动时连续触发。通常我们再滑屏页面,会调用event的preventDefault()可以阻止默认情况的发生:阻止页面滚动
touchend——当手指离开屏幕时触发
touchcancel——系统停止跟踪触摸时候会触发。例如在触摸过程中突然页面alert()一个提示框,此时会触发该事件,这个事件比较少用

//TouchEvent说明:
touches:屏幕上所有手指的信息
targetTouches:手指在目标区域的手指信息
changedTouches:最近一次触发该事件的手指信息
touchend时,touches与targetTouches信息会被删除,changedTouches保存的最后一次的信息,最好用于计算手指信息

//参数信息(changedTouches[0])
clientX、clientY在显示区的坐标
target:当前元素

//事件响应顺序
ontouchstart > ontouchmove > ontouchend > onclick

21.点击元素产生背景或边框怎么去掉

//ios用户点击一个链接,会出现一个半透明灰色遮罩, 如果想要禁用,可设置-webkit-tap-highlight-color的alpha值为0去除灰色半透明遮罩;
//android用户点击一个链接,会出现一个边框或者半透明灰色遮罩, 不同生产商定义出来额效果不一样,可设置-webkit-tap-highlight-color的alpha值为0去除部分机器自带的效果;
//winphone系统,点击标签产生的灰色半透明背景,能通过设置<meta name="msapplication-tap-highlight" content="no">去掉;
//特殊说明:有些机型去除不了,如小米2。对于按钮类还有个办法,不使用a或者input标签,直接用div标签 
a,button,input,textarea { 
    -webkit-tap-highlight-color: rgba(0,0,0,0); 
    -webkit-user-modify:read-write-plaintext-only; //-webkit-user-modify有个副作用,就是输入法不再能够输入多个字符
}   
// 也可以 
* { -webkit-tap-highlight-color: rgba(0,0,0,0); }
//winphone下
<meta name="msapplication-tap-highlight" content="no">

22.美化表单元素

//一、使用appearance改变webkit浏览器的默认外观
input,select { -webkit-appearance:none; appearance: none; }

//二、winphone下,使用伪元素改变表单元素默认外观
//1.禁用select默认箭头,::-ms-expand修改表单控件下拉箭头,设置隐藏并使用背景图片来修饰
select::-ms-expand { display:none; }

//2.禁用radio和checkbox默认样式,::-ms-check修改表单复选框或单选框默认图标,设置隐藏并使用背景图片来修饰
input[type=radio]::-ms-check,
input[type=checkbox]::-ms-check { display:none; }

//3.禁用pc端表单输入框默认清除按钮,::-ms-clear修改清除按钮,设置隐藏并使用背景图片来修饰
input[type=text]::-ms-clear,
input[type=tel]::-ms-clear,
input[type=number]::-ms-clear { display:none; }

23.移动端字体单位font-size选择px还是rem?

// 如需适配多种移动设备,建议使用rem。以下为参考值:
html { font-size: 62.5%; } //10*16 = 62.5%
//设置12px字体 这里注意在rem前要加上对应的px值,解决不支持rem的浏览器的兼容问题,做到优雅降级
body { font-size:12px; font-size:1.2rem; }

24.input标签添加上disable属性在ios端字体颜色不兼容的问题?

input[disabled],input:disabled,input.disabled{

color: #3e3e3e;  
-webkit-text-fill-color: #3e3e3e;  
-webkit-opacity:1;  
opacity: 1;  

}

25.IOS 的光标大小问题

IE:不管该行有没有文字,光标高度与font-size一致。
FF:该行有文字时,光标高度与font-size一致。该行无文字时,光标高度与input的height一致。
Chrome:该行无文字时,光标高度与line-height一致;该行有文字时,光标高度从input顶部到文字底部(这两种情况都是在有设定line-height的时候),如果没有line-height,则是与font-size一致。
IOS中情况和Chrome 相似。

设置字体大小和行高一致,然后通过 padding 撑开大小
只给IE浏览器设置 line-height
-ms-line-height:40px;

26.history.go()和history.back()的用法和区别?

go(-1): 返回上一页,原页面表单中的内容会丢失,是个新页面。
back(): 返回上一页,原页表表单中的内容会保留。
不同的浏览器的后退行为也是有区别的:
chrome和ff浏览器后退页面,会刷新后退的页面,若有数据请求也会提交数据申请。类似于
history.go(-1) 。
而safari(包括桌面版和ipad版本)的后退按钮则不会刷新页面,也不会提交数据申请。类似于
Javascript:history.back()。