mcgrady

mcgrady 查看完整档案

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

做最人性化的交互

个人动态

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

认证与成就

  • 获得 5 次点赞
  • 获得 2 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 2 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2015-12-14
个人主页被 955 人浏览