mcgrady

mcgrady 查看完整档案

南京编辑南京信息工程大学  |  计算机科学与技术 编辑苏州那道网络科技有限公司  |  web前端 编辑填写个人主网站
编辑

做最人性化的交互

个人动态

mcgrady 发布了文章 · 3月1日

javascript中的postMessage

父页面、子页面,两页面不同域,之间对话用到了postMessage。下面为了方便统称为F、C页。

C页按钮的点击事件向F页发送一个消息小C,F页收到消息小C执行逻辑LC,LC执行完毕,F页向C页发送一个消息小F,C页收到消息小F执行逻辑LF。一句话,就是F、C页间相互通信。

可以认为

类似于react中的父子组件通信。

C页js代码:

var btnObj = document.getElementById('buttons');
btnObj.onclick = function(){
     var defaultAdData = {
                 type:'advert', 
                 gameData:{
                     adId: '123'
                 }
         };
     window.parent.postMessage(JSON.stringify(defaultAdData), '*');
    /*我是错误代码:
     var receiveMessage = function(event) {
         var datas = JSON.parse(event.data);
         if (datas.type === "adGivePrize"&&datas.givePrize) {
             alert(‘click’);
         }
     }
     window.addEventListener("message", receiveMessage, false);*/
 }
 /*我是正确代码:
 var receiveMessage = function(event) {
     var datas = JSON.parse(event.data);
     if (datas.type === "adGivePrize"&&datas.givePrize) {
         alert(‘click’);
     }
 }
 window.addEventListener("message", receiveMessage, false);*/

F页js代码:

var receiveMessage = function(event) {
      var datas = JSON.parse(event.data);
      if (datas.type === "advert") {
            var postIframeData = {
                    type:'adGivePrize',
                    givePrize:true
            };
            //iframe发送信息~~~~
            window.frames[0].postMessage(JSON.stringify(postIframeData), '*');
      }
}

window.addEventListener("message", receiveMessage, false);

总之,此方法提供了两个不相干页面的通信,使得外建的项目或者内嵌的iframe,可以互相通信

查看原文

赞 0 收藏 0 评论 0

mcgrady 赞了文章 · 2018-11-21

令人困惑的webpack之entry

使用webpack快一年了,现在1.X版本都过气了,但是感觉自己对它那复杂的配置还是很不熟悉,各种路径,各种loader,各种plugin,又是单页面又是多页面... 在vue-cli出来的时候,都不敢用他的webpack模板,主要就是因为webpack的配置文件看不懂,不敢自己根据需要做修改。现在沉下心来,一点一点的玩弄常用属性和插件,尽力能自如的进行配置。先拿配置中的entry开始。

从最简单开始

最简单的webpack.config.js文件:

module.exports = {
    entry: './app.js',
    output: {
        path: './output',
        filename: 'output-file.js'
    }
}

这个足够简单,进行webpack之后,会在命令的执行目录下新建output目录(如果需要的话),并将打包app.js和它的依赖,生成output-file.js放在output目录中:

如果不希望这里涉及到的路径和执行webpack命令时的具体路径相关,而是希望相对于配置文件的路径的话,就需要使用path模块:

var path = require('path')

module.exports = {
    entry: path.resolve(__dirname, './app.js'),
    output: {
        path: path.resolve(__dirname, './output'),
        filename: 'output-file.js'
    }
}

entry的三种形式

配置文件中entry接受三种形式的值:字符串,数组和对象

对象entry

对象形式如下:

entry: {
    <key>: <value>
    ...
}

最先介绍对象形式,是因为这个是最完整的entry配置,其他形式只是它的简化形式而已。对象中的每一对属性对,都代表着一个入口文件,因此多页面配置时,肯定是要用这种形式的entry配置。

key

key可以是简单的字符串,比如:'app', 'main', 'entry-1'等。并且对应着output.filename配置中的[name]变量

entry: {
    'app-entry': './app.js'
},
output: {
    path: './output',
    filename: '[name].js'
}

上面的配置打包后生成:

key还可以是路径字符串。此时webpack会自动生成路径目录,并将路径的最后作为[name]。这个特性在多页面配置下也是很有用的

entry: {
    'path/of/entry': './deep-app.js',
    'app': './app.js'
},
output: {
    path: './output',
    filename: '[name].js'
}

上面的配置打包后生成:

value

value如果是字符串,而且必须是合理的noderequire函数参数字符串。比如文件路径:'./app.js'(require('./app.js'));比如安装的npm模块:'lodash'(require('lodash'))

entry: {
    'my-lodash': 'lodash'
},
output: {
    path: './output',
    filename: '[name].js'
}

上面的配置打包后生成:

value如果是数组,则数组中元素需要是上面描述的合理字符串值。数组中的文件一般是没有相互依赖关系的,但是又处于某些原因需要将它们打包在一起。比如:

entry: {
    vendor: ['jquery', 'lodash']
}

字符串entry

entry: './app.js'

等价于下面的对象形式:

entry: {
    main: './app.js'
}

数组entry

entry: ['./app.js', 'lodash']

等价于下面的对象形式:

entry: {
    main: ['./app.js', 'lodash']
}

应用

具备了上面的能力,就可以开始配置一个简单的多页面webpack开发环境了。

开始前,要考虑清楚项目目录结构,使用wepback时,一般要有个src源代码目录和一个build的打包目录。

|-build
|-src
webpack.config.js

src目录结构

假设我们有两个页面home和about,两个main.js分别是两个页面的入口文件:

|-src
    |-pages
          |-about
                about.html
                main.js
          |-home
                home.html
                main.js

build目录结构

对于复杂点的webpack项目,先决定打包后的目录结构很重要。webpack就像画笔,打包后的目录就像你打算画的画,要朝着目标去画。

假如我希望“画”是这样的:

|-build
      |-assets
             |-js
                 home.bundle.js
                 about.bundle.js
      home.html
      about.html

有了这个结构,html中如何引入js文件就清楚了,例如在src/pages/home/home.html中:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Home</title>
  </head>
  <body>
    Home Page
  </body>
  <script data-original="assets/js/home.bundle.js"></script>
</html>

webpack配置文件

接下来,朝着build的结构,写配置文件webpack.config.js:

var path = require('path')

module.exports = {
  entry: {
    'assets/js/home': path.resolve(__dirname, './src/pages/home/main.js'),
    'assets/js/about': path.resolve(__dirname, './src/pages/about/main.js')
  },
  output: {
    path: path.resolve(__dirname, './build'),
    filename: '[name].bundle.js'
  }
}

打包

在webpack.config.js目录下执行webpack命令,然后手动将两个html文件从src/pages下拷贝到build目录下,这样在build目录下就是一个打包好的多页面结构了。

后面将用各种插件,让webpack打包全自动化,这里只是一个简单的应用例子来了解entry的用法。

结语

entry一个人能玩的基本就这么多,一些复杂的配置无非是通过变量的形式给其赋值,完成更灵活的配置。entry是webpack的起点,后面所有的文件生成,提取CSS,生成HTML或者是CommonChunk都是在其基础上进行的加工处理。

查看原文

赞 65 收藏 55 评论 8

mcgrady 赞了回答 · 2018-11-21

npm安装警告

其实第二个警告中的提示信息,已经给了我们答案fsevents@1.1.1: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
依赖fsevents想要的是操作系统是darwin,darwin是MacOSX 操作环境的操作系统成份,处理器无限制。而你的当前操作系统是32位的windows操作系统,基于x64处理器。
简而言之,fsevent只能在MacOSX环境下使用,在windows或者linux下使用会出现警告。忽略即可。

关注 3 回答 3

mcgrady 赞了回答 · 2017-03-24

React事件处理函数参数

這兩個函数handleCelsiusChangehandleFahrenheitChange并不是事件处理函数。

事件处理函数的基本语法是像下面这样,传入事件对象,然后针对事件中的值作处理:

handleEvent(e){
  this.setState({value: e.target.value});
}

能这样作必须是对应到可处理的DOM元素上,例如表单元件,文字输入栏、按钮等等,或是一般的DIV等等。

这一行是传入handleCelsiusChange函数,作为TemperatureInput组件的其一个props值,TemperatureInput子组件中可以用this.props.onChange调用这个父组件中的这个函数handleCelsiusChange,这是一种传递父组件中的函数,到子组件的方式,当然,像一般的函数一样,都要先进行bind(绑定)过this。

<TemperatureInput
          scale="c"
          value={celsius}
          onChange={this.handleCelsiusChange} />

真正的事件处理函数在TemperatureInput中,像下面的代码(已简化过):

class TemperatureInput extends React.Component {
  //...

  handleChange(e) {
    this.props.onChange(e.target.value);
  }

  render() {
    //...
    return (
      <fieldset>
        <legend>Enter temperature in {scaleNames[scale]}:</legend>
        <input value={value}
               onChange={this.handleChange} />
      </fieldset>
    );
  }
}

事件的触发是在下面这个DOM元素:

 <input value={value}
               onChange={this.handleChange} />

onChange触发时,调用handleChange像下面这样:

handleChange(e) {
    this.props.onChange(e.target.value);
}

然后把e.target.value传到this.props.onChange对应的函数值,也就是上一层组件的handleCelsiusChange函数中。

handleCelsiusChange函数接收到后,会用这个传入值来更动state值,然后进行重渲染。

会造成误解是因为它把props的名称写得与事件用的onChange一样,父组件用了onChange这个props名称来传递函数作为值,实际上如果怕误解可以用别的名称,例如changeMethodparentMethod,效用是一样的。

这是一种子组件传递数值到父组件的方式,大概也是内建可使用的一种方式。是一种迂廻的作法,两边(父组件与子组件)都要对应好才会正常运作,只适用于结构简单的组件中。

有点相似的问答: https://segmentfault.com/q/10...

关注 3 回答 2

mcgrady 赞了文章 · 2017-02-06

react学习(一)

<script data-original="react.js"></script>
<script data-original="react-dom.js"></script>
<script data-original="babel.min.js"></script>

JSX

<div id="example"></div>

<script type="text/babel">
  ReactDOM.render(
    <span>Hello React!</span>,
    document.getElementById('example')
  );
</script>
  1. ReactDOM.render方法接受两个参数:一个虚拟 DOM 节点和一个真实 DOM 节点,作用是将虚拟 DOM 挂载到真实 DOM。

React 组件语法

<div id="example"></div>

<script type="text/babel">
   class MyTitle extends React.Component {
      render() {
         return <h1>Hello World</h1>;
   }
 };

 ReactDOM.render(
   <MyTitle/>,
   document.getElementById('example')
 );
</script>

或者:

<script type="text/babel">
      var MyTitle = React.createClass({
        render () {
              return <h1>Hello World2</h1>
        }
      });

      ReactDOM.render(
        <MyTitle/>,
        document.getElementById('example')
      );
</script>


  1. class MyTitle extends React.Component是 ES6 语法,表示自定义一个MyTitle类,该类继承了基类React.Component的所有属性和方法。

  2. React 规定,自定义组件的第一个字母必须大写,比如MyTitle不能写成myTitle,以便与内置的原生类相区分。

  3. 每个组件都必须有render方法,定义输出的样式。

  4. <MyTitle/>表示生成一个组件类的实例,每个实例一定要有闭合标签,写成<MyTilte></MyTitle>也可。

组件的参数

<div id="example"></div>

<script type="text/babel">
    class MyTitle extends React.Component {
        render() {
           return <h1 style={{color: this.props.color}}>Hello World</h1>;
        }
   };

  ReactDOM.render(
      <MyTitle color="red" />,
      document.getElementById('example')
 );
</script>
  1. 组件内部通过this.props对象获取参数。

组件的状态 this.state

实例一

<div id="example"></div>

<script type="text/babel">
 class MyTitle extends React.Component {
constructor(...args) {
  super(...args);
  this.state = {
    name: '访问者'
  };
}

handleChange(e) {
  let name = e.target.value;
  this.setState({
    name: name
  });
}

 render() {
  return <div>
    <input type="text" onChange={this.handleChange.bind(this)} />
    <p>你好,{this.state.name}</p>
  </div>;
 }
};

ReactDOM.render(
   <MyTitle/>,
  document.getElementById('example')
);
</script>

实例二

<div id="example"></div>

<script type="text/babel">    
 class MyTitle extends React.Component {
constructor(...args) {
  super(...args);
  this.state = {
    text: 'World',
    isClicked: false
  };
}

handleClick() {
  let d = new Date();
  let time = d.getFullYear() + '年' + (d.getMonth() + 1) + '月' + d.getDate() + '日';
  let isClicked = !this.state.isClicked;
  console.log(isClicked);
  this.setState({
    isClicked: isClicked,
    text: isClicked ? time : 'World'
  });
}

render() {
  return <h1 onClick={this.handleClick.bind(this)}>
    {'Hello ' + this.state.text}
  </h1>;
}

};

ReactDOM.render(
   <MyTitle/>,
   document.getElementById('example')
);
</script>

React 组件的生命周期

componentWillMount():组件加载前调用
componentDidMount():组件加载后调用
componentWillUpdate(): 组件更新前调用
componentDidUpdate(): 组件更新后调用
componentWillUnmount():组件卸载前调用
componentWillReceiveProps():组件接受新的参数时调用

实例一

<div id="example"></div>

<script type="text/babel">
  class MyList extends React.Component {
    constructor(...args) {
      super(...args);
      this.state = {
        loading: true,
        error: null,
        data: null
      };
    }

  componentDidMount() {
    const url = 'https://api.github.com/search/repositories?q=javascript&sort=stars';
    $.getJSON(url)
     .done(
      (value) => this.setState({
        loading: false,
        data: value
      })
    ).fail(
      (jqXHR, textStatus) => this.setState({
        loading: false,
        error: jqXHR.status
      })
    );
  }

  render() {
    
  if (this.state.loading) {
    return <span>Loading...</span>;
  } else if (this.state.error !== null) {
    return <span>Error: {this.state.error}</span>;
  } else {
    /* 你的代码填入这里 */
    return (
      <div>
        <p>API 数据获取成功</p>
        <p>改写代码,将结果显示在这里</p>
      </div>
    );
  }
  }
};

  ReactDOM.render(
    <MyList/>,
    document.getElementById('example')
  );
</script>

实例二

<div id="example"></div>

<script type="text/babel">    
var Hello = React.createClass({
   getInitialState: function () {
      return {
        opacity: 1.0
      };
   },

   componentDidMount: function () {
      this.timer = setInterval(function () {
         var opacity = this.state.opacity;
         opacity -= .05;

         if (opacity < 0.1) {
           opacity = 1.0;
         }

         this.setState({
            opacity: opacity
         });
      }.bind(this), 100);
   },

   render: function () {
      return (
        <div style={{opacity: this.state.opacity}}>

          Hello {this.props.name}
        </div>
      );
   }
});

ReactDOM.render(
    <Hello name="world"/>,
    document.getElementById('example')
);
</script>   


为组件添加外部css样式时,类名应该写成className而不是class;添加内部样式时,应该是style={{opacity: this.state.opacity}}而不是style="opacity:{this.state.opacity};"。

实例三:组件的嵌套

<div id="example"></div>

<script type="text/babel">    
var Search = React.createClass({
   render: function () {
      return (
        <div>
           {this.props.searchType}: <input type="text" />
           <button>Search </button>
        </div>
      );
   }
});

var Page = React.createClass({
   render: function () {
      return (
        <div>
           <h1>Welcome!</h1>
           <Search searchType = "Title" />
           <Search searchType = "Content" />
        </div>
      );
   }
});

ReactDOM.render(
    <Page/>,
    document.getElementById('example')
);
</script>

ref

有时需要从组件获取真实 DOM 的节点,这时就要用到 ref 属性

实例:

<div id="example"></div>
<script type="text/babel">    
var MyComponent = React.createClass({
  handleClick: function() {
    this.refs.myTextInput.focus();
  },
  render: function() {
    return (
      <div>
        <input type="text" ref="myTextInput" />
        <input type="button" value="Focus the text input" onClick={this.handleClick} />
      </div>
    );
  }
});

ReactDOM.render(
  <MyComponent />,
  document.getElementById('example')
);
</script>
查看原文

赞 1 收藏 2 评论 0

mcgrady 赞了回答 · 2017-01-20

如何在H5页面中 增加按钮点击分享到微信朋友圈功能

弹出一个图片,指导用户点击微信自带的分享按钮

关注 8 回答 8

mcgrady 赞了文章 · 2016-12-13

浅谈javascript中的prototype

本人博客:【www.xiabingbao.com

在本文中,我们讲解prototype的内容主要由:什么是prototype,prototype与函数之间的关系,prototype与实例对象之间的关系,使用proto实现一个简单的继承。

1. prototype的简要介绍

在javascript中,创建的每个函数天生都自带着一个prototype属性。这里我们要强调的是:这个prototype属性是一个指针,指向一个对象,在这里,我们称指向的这个看不到但确实存在的对象为原型对象。其实可以用下面一个简单的例子来说明:

var proto = {
    name : 'wenzi',
    age : 25
}
function Person(){

}
Person.prototype = proto;

这样Person.protptype就指向到了proto,如果还有其他的引用(A)也指向到了proto,那么这个引用和Person.prototype就是相等的:A==Person.prototype

prototype指向的这个对象是真实存在的,可能很多的同学对prototype属性和原型对象有些混淆,我们在这里把原型对象叫做大SB大SB与普通对象的一个不同之处就是,他也有一个天生的属性:constructor,这个constructor重新指回到了函数。不过,既然大SB也是一个对象,那么它也是继承于Object的,拥有Object所有的方法和属性,比如toString()等。

大SB = {
    constructor : Person,
    say : function(){
        return "hello world";
    }
}

那么现在我们就能得到两个引用关系:

大SB = Person.prototype; // 原型对象 = 函数.prototype;
Person = 大SB.constructor(Person.prototype.constructor); // 函数 = 原型对象.constructor(函数.prototype.constructor)

img

从运行的代码中,可以验证我们的观点,Person.prototype的类型是object,Person.prototype.constructor重新指回到了Person。
其实在实例对象中,也存在着一个属性指向到大SB中:

var John = new Person();
大SB = John.__proto__;

即 John.__proto__ 和 Person.prototype 指向的是同一个引用:John.__proto__==Person.prototype

2. 对象实例和prototype中的属性

在构造函数能设置属性和方法,在prototype中也能设置属性和方法,那new出的对象使用的是哪个呢?我们来看一个例子:

function Person(){
    this.name = "wenzi";
}
Person.prototype.name = "bing";
Person.prototype.say = function(){
    return "hello world";
}
var John = new Person();
alert(John.name);  // "wenzi"
alert(John.say()); // "hello world"

从运行的结果我们可以看到,John.name输出的是构造函数中的属性值"wenzi",John.say()输出的是"hello world"。这是因为,当读取某个对象的属性值时,会首先在实例对象中进行搜索,若搜索到则直接进行返回;若搜索不到则去原型对象中进行寻找。因此在使用John调用say()方法时是正确的,不会报错。而且是进行了两次的搜索。

虽然say()是挂载在prototype上,John也同样能直接进行访问。但是,我们不能直接对John.say进行修改从而影响prototype的say()方法,如下:

John.say = function(){
    return "this has changed";
}
John.say(); // "this has changed"
var Tom = new Person();
Tom.say(); // "hello world"

从运行John.say()的结果来看,say()方法确实发生了变化。但是,当我们再new出一个新对象Tom时,Tom.say()返回的还是"hello world",这是为什么呢?

因为,对John.say()进行修改时,不是修改了prototype上的say(),而是给John这个实例对象添加了一个say()方法,从而屏蔽了prototype上的say()方法,由上面的寻找顺序我们可以知道,若实例对象存在这个方法就直接返回了,不再去原型对象上寻找。而new出的新对象Tom在调用say()方法时,Tom自己是没有say()方法的,只能去原型对象上寻找,因此返回的还是"hello world"。所以,对John.say进行修改时,不是修改了prototype上的say()方法,而是给John添加了实例方法,屏蔽掉了prototype上的方法而已。那如何才能修改prototype上的方法呢,好办,直接在prototype上修改:

Person.prototype.say = function(){
    return "wenzi's blog";
}

这样就能修改prototype的say()方法了。

3. 重写prototype

在上面的例子中,我们都是大部分给prototype添加属性或方法,本来prototype指向的是大SB,若我们给prototype添加属性或方法时,就是给大SB添加属性或方法:

Person.prototype.name = "wenzi"; 

大SB里其他的属性和方法是不受影响的,constructor依然指回到Person。但是,若我们这样写:


Person.prototype = {
    name : "wenzi",
    say : function(){
        return "my name is name"
    }
}

就是对Person的prototype重写了,让prototype进行了重新的指向,本来Person.prototype指向的是大SB,可是现在却指向了CTM,而CTM里也没有constructor属性指回到Person,若是想重新指回到Person,还得手动添加一个constructor属性:

Person.prototype = {
    constructor : Person, // 重新指回到Person
    name : "wenzi",
    say : function(){
        return "my name is name"
    }
}

这样就能手动构造出新的原型对象了,new出的实例对象也能使用这个prototype上的属性和方法。

但是这里还存在一个问题,若之前已经有new出的实例对象,然后再修改Person.prototype,之前的实例对象是无法使用新的原型对象(CTM)上的属性和方法的,因为之前的实例对象的__proto__指向的依然是大SB。因此,请慎重重写prototype

4. 原型继承

说到prototype就不得说prototype继承,我们通过给prototype上添加属性和方法,就能使该构造函数所有的实例对象拥有属性和方法。我们先来看下面的代码:

function Father(){
    this.name = "father";
    this.age = 43;
}
Father.prototype.job = "Doctor";
Father.prototype.getName = function(){
    return this.name;
}

function Son(){
    this.name = "son";
}
Son.prototype = new Father(); // Son的prototype指向Father的实例对象
var John = new Son();
for(var k in John){
    console.log(k+' : '+john[k]);
}
/*
    输出结果:
    name : son
    age : 43
    job : Doctor
    getName : function (){
        return this.name;
    }
*/

从上面的例子中可以看到,Son的实例对象继承了Father中所有的属性和方法。当然,若Son的对象实例中存在的,还依然保留。不过Son原型中的属性和方法是会被彻底覆盖的。我们来分析一下这是为什么?

Son的prototype指向的是Father的一个实例,我们把这拆成两步:

var father = new Father();
Son.prototype = father;

在实例father中既有name,age属性,也有Father.prototype中的属性和方法,我们对father循环看一下:

for(var k in father){
    console.log(k, father[k]);
}
/*
    name father
    age 43
    job Doctor
    getName Father.getName()
*/

由于constructor是不可枚举的类型,在for~in循环里是输出不了constructor的,其实father是这样的:

father = {
    constructor : Father,
    name : father
    age : 43
    job : Doctor
    getName : Father.getName()
}

因此Son的prototype指向的就是上面father的内容。到此,Son的prototype中的constructor的指向也发生了改变,本来Son.prototype是指向到Son的,现在指向到了Father。即完全重写了Son的prototype,重新指向了另一个引用。所以,我们就能知道为什么对象实例中的属性能够保存,而prototype中的会彻底被删除了。

如果想给Son添加原型方法或属性,那只能是在Son.prototype = new Father();之后进行添加:

Son.prototype = new Father();
Son.prototype.getJob = function(){
    return this.job;
}

这样就能添加上getJob方法了。

到这里,会有人问,如果Son.prototype = Father.prototype会怎么样呢?我们刚才也说了Father.prototype指向的是大SB,而大SB也是一个实例对象,是Object的一个实例,这就相当于:

Son.prototype == 大SB; // 指向了大SB

因此,如果是Son.prototype = Father.prototype的话,那么Son只能继承到Father.prototype上的jobgetName(),其他的属性是获取不到的。

5. 总结

本文简单的介绍了一下prototype,让我们能对prototype有个浅显的认识。当然,博主才疏学浅,文章里会有很多疏漏和不完善的地方,欢迎大家批评指正。

本人博客:【www.xiabingbao.com

欢迎大家关注我的微信公众号:wenzichel
wenzichel

查看原文

赞 6 收藏 32 评论 3

mcgrady 发布了文章 · 2016-12-13

移动端点击输入框,enter键问题

html5提供了input type="search"这种类型,所以我们点击某些输入框的时候,enter键会从“换行”变成“搜索”。

1.何时为换行

<div class="after">
    <input type="search" placeholder="职位/技能/姓名" />
    <span class="cancel">取消</span>
</div>

图片描述

这种情况,input外面木有嵌form套套的时候,enter键是原来的样子。并且点击换行没有跳转

2.何时为搜索

<div class="after">
    <form action="">
        <input type="search" placeholder="职位/技能/姓名" />
        <span class="cancel">取消</span>
    </form>
</div>

图片描述

input外面嵌套form套套的时候,enter键会变为搜索,并且点击搜索会跳转到相应的action

查看原文

赞 0 收藏 3 评论 0

mcgrady 回答了问题 · 2016-08-01

安卓下视频播放问题

绝对定位,没撑开吧。

关注 2 回答 1

mcgrady 关注了问题 · 2016-08-01

安卓下视频播放问题

想问下在h5安卓机下
为什么在视频上给元素加绝对定位
想浮到他上面会无效???
ios下是没有问题的
clipboard.png

关注 2 回答 1

mcgrady 赞了回答 · 2016-08-01

你们有这样的情况吗?我做前端开发,做出来跟UI设计差不多,给后端开发人员后又变成另一个模样

哈哈o(^▽^)o,看到这个问题我笑了。

偷偷的告诉你,我们后端人员是能读懂前端代码的,稍微理解一些。有时候,会悄悄地修改一下,这样,产生的差异,我们,就有理由找你们前端说话了。(●'◡'●)后端开发有些枯草。我只想和你们说说话而已。

关注 11 回答 11

mcgrady 赞了回答 · 2016-08-01

解决用jquery控制input聚焦时候,光标在最前面?

jquery类似的思路

$temp = $("#input_id").val();
$("#input_id").val("").focus().val($temp);

关注 2 回答 1

mcgrady 赞了回答 · 2016-08-01

解决用jquery控制input聚焦时候,光标在最前面?

jquery类似的思路

$temp = $("#input_id").val();
$("#input_id").val("").focus().val($temp);

关注 2 回答 1

mcgrady 赞了回答 · 2016-08-01

解决用jquery控制input聚焦时候,光标在最前面?

jquery类似的思路

$temp = $("#input_id").val();
$("#input_id").val("").focus().val($temp);

关注 2 回答 1

mcgrady 关注了问题 · 2016-08-01

解决加载HTML网页,在网页上文字居中,在手机端用WebView加载出来不居中

加载HTML网页,在网页上文字居中,在手机端用WebView加载出来不居中,这个应该应该怎么解决,手机端调整还是后台调整

关注 10 回答 10

mcgrady 回答了问题 · 2016-08-01

解决加载HTML网页,在网页上文字居中,在手机端用WebView加载出来不居中

肯定是前端的锅啊!

关注 10 回答 10

mcgrady 赞了文章 · 2016-07-27

切图崽的自我修养-优化图片加载流程

前言

优化! 又是优化!

切图崽们作为整个web应用的纽带,连接着用户行为和机器性能. 而优化的最终意义,在于在这两者之间取得一个最佳的平衡点.

对于图片资源的加载来说,更是如此. 今天我们就来简单说说,项目开发中常见的图片加载优化方式.


预加载

1.遮罩大法

我们经常用jquery, jquery中$(function){})实际上是DOMContentLoaded事件完成的回调,只是完成了DOM树的构建. 诸如Css的渲染以及页面内图片等资源的下载不一定完成了.所以如果此时呈现页面,页面会非常难看.

为了解决这个问题,为了从设计和行为的角度提高用户体验,我们可以在图片等重要资源完全下载完之前,对页面加上较为美观的遮罩,并且弹出loading提示告知用户资源正在loading.等到图片完全加载完,才移除遮罩和加载动画.

具体的实现思路如下:

  1. $(function(){})调用之后,先弹出蒙板加上loading动画用来提示用户正在loading中

  2. 对页面中需要预加载的IMG元素进行下载var img = new Image(); img.data-original="xx.jpg"

  3. 图片下载完成会有一个onload的回调img.onload = function(){...}

  4. 在这个回调中移除loading动画以及遮罩

这样就可以给用户带来顺滑如丝般的操作体验了,再也不用担心用户看到那些正在下载的未显示完全的丑的要死图片了.

我们的口号是: 要么就不给你看,要么就给你看最好的
应用场景: 请在"首屏中存在图片的动画,或者和你对接的UI设计师极其强势"的情况下使用

2.有码大法

有码大法和遮罩大法略微有区别,具体实现思路如下:

  1. 首先对你需要预加载的图片准备两张,一张是高清一张低清. 比如girl_hd大小为60kb. 另一张是girl, 大小是6kb.

  2. html页面中需要预加载的image标签的src地址写的是低清的地址<img data-original="girl.jpg" class="hd-replace">

  3. 因为低清图很小,很快就能被加载出来.

  4. $(function(){})调用之后,获取页面需要高清替换的img的src(girl.jpg),以此src为基准拼接字符串(+'_hd.jpg')获得高清图的地址(girl_hd.jpg),然后用下载该高清图var img = new Image(); img.data-original=“girl_hd.jpg”

  5. 图片下载完成会有一个onload的回调img.onload = function(){...}

  6. 回调中替换掉页面中img的src, 所以现在页面上的image标签为 <img data-original="girl_hd.jpg" class="hd-replace">

我们的口号是: 想看无码高清,请先看有码低清
应用场景: 请在"首屏中出现大量图片,且尺寸都不小"的情况下使用


懒加载

如果你仔细看了上面预加载的思路,一定往我脑袋上拍砖: 遮罩大法也好,有码大法也好,这并没有提高项目的加载速度啊,最后该下载的图片还不是得下载. 没错,懒加载只是改变了用户的操作感受,实际上项目的加载速度并没有提高. 但是,现在要说的懒加载,可是实实在在的提高了项目的加载速度哦.

什么是懒加载,一句话来解释, 就是图片按需加载.

大家一定刷过微博,微博的照片墙就是懒加载的最佳示例.一开始显示的照片并不多,只有用户下拉,拉到底部的位置, 照片墙才会被拉长,新的图片才会被加载.

操作思路:

  1. 监听滚动条scroll事件(当然touchmove事件也可以)

  2. 每次事件触发的时候,判断当前照片墙的位置

  3. 如果照片墙已经被刷到了底部某个临界位置点

  4. Js下载新出现的图片,var img = new Image(); img.data-original="xx.jpg"

  5. 下载完成有一个onload的回调img.onload = function(){...}

  6. 在下载完成的回调中向页面中插入已经下载好的图片

当然,根据项目不同,会有各种各样的懒加载方式.但核心是不变的:即页面初始加载的时候,只加载满足用户需求的最小数量的资源. 拿照片墙举例,可能用户的微博里有500张照片,如果你在页面加载的时候就加载500张,用户会卡到爆炸(因为后台一直处于图片下载状态). 如果页面加载的时候只初始加载20张图片,其他的图片通过用户自己的操作(滚动下拉),来按需加载,会极大提升项目运行的流畅程度.


结语

虽然预加载还是懒加载实现原理都非常简单,给我的启示确是巨大的:

  • 预加载除了改善用户的操作感受,其深层次的核心其实在于:对资源进行碎片化加载, 即预加载其实可以出现在任何时间段,当用户鼠标很长时间没移动的时候,我可不可以偷偷下载两张图片?在用户目前没有进行大量运算操作的时候,我可不可以偷偷下载两张图片?当用户当前在一个很精简的登陆界面的时候,我可不可以偷偷下载他登陆成功跳转到的页面的几张图片?等等等等

  • 懒加载的深层次核心在于:按需, 按需这个词已经被深深刻在我脑子里了. 现在回想起来,很多很多优化方式都是围绕着按需来展开的. 按需加载Js,按需加载图片等等
    首先,我们必须保证项目第一时间的加载速度,能让用户在最短的时间内看到页面和内容.

其次,尽量保证当前页面的精简程度,不去做无意义的加载. 只有当用户真正需要时,我们才展现给他.

各自的优缺点在于:

预加载:

  • 优点:如果提前下好了图片,等到这张图片需要用到时,可以秒开.

  • 缺点:下载图片的时候会影响项目的加载完成时间,会影响项目运行的流畅程度

懒加载在于:

  • 优点: 保证用户加载的项目是最精简的,最快的, 所下载资源是最少的

  • 缺点: 如果用户的操作触发了懒加载,那么需要等待资源下载到完成的时间,同时资源下载期间,操作流畅度降低

说到底,项目的优化是没有银弹的,这一部分的高效很可能导致另一部分的低效.A项目的优化方法照搬来B项目可能一文不值.
所以我们切图崽们能做的,就是深刻理解这些技术的原理,并且在项目中吸收经验,只有深刻地理解了各项技术的优劣,只有深刻的理解了用户的需求以及行为习惯,才能针对特定的项目,特定的场景,进行最适合的处理.

查看原文

赞 6 收藏 56 评论 6

mcgrady 赞了文章 · 2016-07-26

Node.js安装

安装包准备

准备好NodeJS安装包:

/opt/node-v4.2.3-linux-x64.tar.gz

解压安装

执行以下命令解压:

cd /opt
tar zxvf node-v4.2.3-linux-x64.tar.gz

移动到指定目录:

mkdir yliyun
mv node-v4.2.3-linux-x64 /opt/yliyun/node 

修改PATH:

vim /etc/profile

添加以下内容:

export PATH=/opt/yliyun/node/bin:$PATH

执行以下命令:

source /etc/profile

安装pm2

执行以下命令:

npm install -g pm2
查看原文

赞 2 收藏 4 评论 1

mcgrady 发布了文章 · 2016-07-26

JS删除数组里指定值的问题

JS删除数组里指定值的问题

标签(空格分隔): JS splice


本文是了解了js的splice函数,为大家写的一些心得

1.splice

splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目。
注释:此函数会改变原数组的值

函数用法arrayObject.splice(index,howmany,item1,.....,itemX)
index是必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。
howmany是必需。要删除的项目数量。如果设置为 0,则不会删除项目。
item1,.....,itemX可选。向数组添加的新项目。

例子 1
在本例中我们将删除位于 index 2 的元素:

<script type="text/javascript">
    var arr = new Array(6)
    arr[0] = "George"
    arr[1] = "John"
    arr[2] = "Thomas"
    arr[3] = "James"
    arr[4] = "Adrew"
    arr[5] = "Martin"
    
    document.write(arr + "<br />")
    arr.splice(2,1)
    document.write(arr);
</script>

输出结果是 :

George,John,Thomas,James,Adrew,Martin
George,John,James,Adrew,Martin

例子 2
在本例中我们将删除位于 index 2 的元素并在此处添加一个Arthur的字符串:

<script type="text/javascript">

var arr = new Array(6)
arr[0] = "George"
arr[1] = "John"
arr[2] = "Thomas"
arr[3] = "James"
arr[4] = "Adrew"
arr[5] = "Martin"

document.write(arr + "<br />")
arr.splice(2,1,"Arthur")
document.write(arr)

</script>

输出结果是 :

George,John,Thomas,James,Adrew,Martin
George,John,Arthur,James,Adrew,Martin
查看原文

赞 0 收藏 1 评论 0

mcgrady 回答了问题 · 2016-03-30

CSS3动画兼容性问题,如何解决?

不会吧,没理由safiri不支持啊

关注 4 回答 3