首頁 >web前端 >js教程 >超級給力的JavaScript的React框架入門教學_基礎知識

超級給力的JavaScript的React框架入門教學_基礎知識

WBOY
WBOY原創
2016-05-16 15:51:541816瀏覽

 React 是 Facebook 裡一群牛 X 的碼農折騰出的牛X的框架。 實作了一個虛擬 DOM,用 DOM 的方式將需要的元件秒加,用不著的秒刪。 React 扮演著 MVC 結構中 V 的角色, 不過你要是 Flux 搭配使用, 你就有一個很牛X的能讓輕鬆讓 M 和 V 同步的框架了,Flux 的事以後再說~
組件們

在 React 中,你可以建立一個有特殊功能的元件,這在 HTML 元素裡你是打著燈籠也找不到的,像是這個教學裡的下拉導覽。每個組件都有自己的地盤(scope),所以我們定義一個組件後可以反覆的用反覆的用,根本就不需要和別的組件交互!

每個元件都有一個功能叫 render , 它可以高效率的返回要射到瀏覽器上 HTML。我們也可以呼叫 React 的其他元件,這意味著這一入 React 深似海,哦,不對,是只有想不到,沒有做不到。

JSX

如果你常常注意到React你或許會發現有個東西叫做JSX。 JSX允許我們在Javascript中寫HTML,而不是用HTML包含Javascript。它能幫助我們快速開發,因為我們不用擔心字串和換行等。你可以在瀏覽器中運行JSX,但是不推薦,因為這樣會減慢你的頁面速度。 gulp和grunt為你的預處理任務提供了一個JSX解釋器,所以如果你想使用JSX,我建議開啟這個功能。
使用 JSX

如前面所說,JSX每一個元件都有一個render 方法,這個方法產生一個"ViewModel(視圖模型)" - 在返回HTML到元件之前,你可以將這個模型(ViewModel)的資訊放入視圖中,意味著你的HTML會根據這個模型動態改變(例如一個動態清單)。


一旦你完成了操作,就可以回到你想渲染(render)的東西,我們使用JSX來做如此簡單
 

var ExampleComponent = React.createClass({
  render: function () {
    return (
      <div className="navigation">
        Hello World!
      </div>
      );
  }
});

如果你要在你的瀏覽器中執行這段程式碼,只會得到語法錯誤的提示,因為在Javascript中字元要用引號包含起來。當你在程式碼上執行JSX解釋器,它將被轉換成這樣
 

var ExampleComponent = React.createClass({
  render: function () {
    return (
      React.createElement('div', {className: 'navigation'}, 'Hello World!')
      );
  }
});

可以點此測試範例 - 我使用的是瀏覽器JSX解釋器(這並不推薦,但是為了JSFiddle)。

運行了! JSX解釋了你使用 React.createElement 產生的所有DOM節點,產生了節點類型、參數和內容。你可以不用JSX,但這意味著你必須要手動寫 React.createElement 以外的所有DOM節點。無數例子告訴我要使用JSX。

你一定很疑惑我為什麼在DOM上使用 className 來取代 class。這是因為 class 是Javascript的保留詞。當JSX解釋你的程式碼時就會改變這節點上的所有屬性到一個物件上,但你不能把物件當作屬性!

將變數用在屬性上
如果你想動態改變元件的樣式類別(或任何其他的屬性值),你可以使用變數. 不過你不能只是傳進去一個變數名稱,你還要將其用一對大括弧包起來, 這樣JSX就能知道這是一個外部的變數了.
 

var ExampleComponent = React.createClass({
render: function () {
  var navigationClass = 'navigation';
  return (
    <div className={ navigationClass }>
      Hello World!
    </div>
    );
}
});

你可以在這裡看到這個功能.
初始的渲染器
當你最開始要渲染一個React元件時,你需要告訴React是要渲染什麼元件,還要製定一個現有的DOM節點以表示在哪裡渲染這個元件. 為此你會要使用React.render函數.
 

var ExampleComponent = React.createClass({
render: function () {
  return (
    <div className="navigation">
      Hello World!
    </div>
    );
}
});
React.render(<ExampleContent />, document.body);

他將會在body節點上渲染元件-簡單吧! 從此你就可以像通常那樣呼叫其他的元件了, 或者如果你希望的話,可以使用render函數許多次, 如果你不想使用React來渲染整個頁面,但仍然想要使用多個組件.

一個組件的基礎

組件可以擁有自己的「狀態」。這使我們能夠重複多次使用相同的元件但卻讓它們看起來完全不同,因為每個元件實例的狀態是唯一的。

當你透過傳遞屬性到一個元件時這些稱為屬性。不要只局限於 HTML 屬性,你可以傳遞任何你想傳遞的東西,並在元件內透過 this.props 存取它。這樣使得我們能夠重複使用相同的元件但傳遞一組不同的屬性,例如元件的「配置」。
屬性

根據我們前面「Hello World!」的例子,我們在 HTML 節點上有 className 的屬性。在元件內部,我們可以使用 this.props.classname 來存取這個值,但如同前面所述,你可以傳遞任何你喜歡的內容。對於我們的下拉來說,我們需要將導航配置為對象,元件將使用它作為要渲染的配置。讓我們開始吧—
 

var navigationConfig = [];
 
var Navigation = React.createClass({
  render: function () {
    return (
      <div className="navigation">
         
      </div>
      );
  }
});

 
React.render(75a8dba852246be4150860216e62d5c1, document.body);

如果现在能访问 this.props.config 的话,我们会受到一个空数组(navigationConfig 的值)。在我们进入到真正导航的编码前先让我们说明一下状态。

状态

如之前所讨论的,每一个组件都有其自己的”状态”。当要使用状态时,你要定义初始状态,让后才可以使用 this.setState 来更新状态。无论何时状态得到了更新,组件都会再一次调用 render 函数,拿新的值去替换或者改变之前渲染的值。这就是虚拟 DOM 的奥义 - 计算差异的算法实在 React 内部完成的,因此我们不用去以来 DOM 的更新了(因为 DOM 很慢)。React 会计算出差异并产生一堆指令的集合 (例如,加入向”navigation__link“加入”active“类,或者移除一个节点),并在 DOM 上执行它们。

使用导航,我们将下拉菜单打开保持在状态中。为此,添加一个 getInitialState 函数到类配置对象上,并返回带有我们想要的初始状态的对象。
 

var navigationConfig = [];
 
var Navigation = React.createClass({
  getInitialState: function () {
    return {
      openDropdown: -1
    };
  },
  render: function () {
    return (
      <div className="navigation">
         
      </div>
      );
  }
});
 
React.render(<Navigation config={ navigationConfig } />, document.body);

你会发现我们将其设置成了-1。当我们准备去打开一个下拉菜单时,将使用状态里面的配置数组中的导航项的位置,而由于数组索引开始于0,我们就得使用 -1 来表示还没有指向一个导航项。

我们可以使用 this.state 来访问状态,因此如果我们去观察 atthis.state.openDropdown,应该会有 -1 被返回。

组件的生命周期

每个组件都有其“生命周期”—这是一系列你可以在组件配置里定义的函数,它们将在部件生命周期内被调用。我们已经看过了 getinitialstate 一它只被调用一次,在组件被挂载时调用。
componentWillMount

当组件要被挂载时这个函数被调用。这意味着我们可以在此运行组件功能必须的代码。为由于 render 在组件生命周期里被多次调用,我们一般会把只需要执行一次的代码放在这里,比如 XHR 请求。
 

var ExampleComponent = React.createClass({
  componentWillMount: function () {
    // xhr request here to get data
  },
  render: function () {
    // this gets called many times in a components life
    return (
      <div>
        Hello World!
      </div>
      );
  }
});

componentDidMount

一旦你的组件已经运行了 render 函数,并实际将组件渲染到了 DOM 中,componentDidMount 就会被调用。我们可以在这儿做任何需要做的 DOM 操作,已经任何需要依赖于组件已经实际存在于 DOM 之后才能做的事情, 例如渲染一个图表。你可以通过调用 this.getDOMNode 在内部访问到 DOM 节点。
 

var ExampleComponent = React.createClass({
  componentDidMount: function () {
    var node = this.getDOMNode();
    // render a chart on the DOM node
  },
  render: function () {
    return (
      <div>
        Hello World!
      </div>
      );
  }
});

componentWillUnmount

如果你准备吧组件从 DOM 移除时,这个函数将会被调用。这让我们可以在组件背后进行清理,比如移除任何我们已经绑定的事件监听器。如果我们没有在自身背后做清理,而当其中一个事件被触发时,就会尝试去计算一个没有载入的组件,React 就会抛出一个错误。
 

var ExampleComponent = React.createClass({
  componentWillUnmount: function () {
    // unbind any event listeners specific to this component
  },
  render: function () {
    return (
      <div>
        Hello World!
      </div>
      );
  }
});

组件方法

React 也为组件提供了更方便我们工作的方法。它们会在组件的创建过程中被调用到。例如getInitialState,能让我们定义一个默认的状态,这样我们不用担心在代码里面对状态项目是否存在做进一步的检查了。
getDefaultProps

当我们创建组件时,可以按照我们的想法为组件的属性定义默认值。这意味着我们在调用组件时,如果给这些属性设置值,组件会有一个默认的“配置”,而我们就不用操心在下一行代码中检查属性这类事情了。

当你定义了组件时,这些默认的属性会被缓存起来,因而针对每个组件的实例它们都是一样的,并且不能被改变。对于导航组件,我们将配置指定为一个空的数组,因而就算没有传入配置,render 方法内也不会发生错误。
 

var Navigation = React.createClass({
  getInitialState: function () {
    return {
      openDropdown: -1
    };
  },
  getDefaultProps: function () {
    return {
      config: []
    }
  },
  render: function () {
    return (
      <div className="navigation">
         
      </div>
      );
  }
});
<p>React.render(<Navigation config={ navigationConfig } />, document.body);</p> 

propTypes

我们也可以随意为每一个属性指定类型。这对于我们检查和处理属性的意外赋值非常有用。如下面的dropdown,我们指定只有数组才能放入配置。
 

var Navigation = React.createClass({
  getInitialState: function () {
    return {
      openDropdown: -1
    };
  },
  getDefaultProps: function () {
    return {
      config: []
    }
  },
  propTypes: {
    config: React.PropTypes.array
  },
  render: function () {
    return (
      <div className="navigation">
         
      </div>
      );
  }
});
 
React.render(<Navigation config={ navigationConfig } />, document.body);

mixins

我们也可以在组件中加入 mixins。这是一个独立于 React 的基本组件(只是一个对象类型的配置)。这意味着,如果我们有两个功能类似的组件,就可以共享一个配置了(如果初始状态相同)。我们可以抽象化地在 mixin 中建立一个方法,这样就不用把相同的代码写上两次了。
 

var ExampleMixin = {
  componentDidMount: function () {
    // bind some event listeners here
  },
  componentWillUnmount: function () {
    // unbind those events here!
  }
};
 
var ExampleComponent = React.createClass({
  mixins: [ExampleMixin],
  render: function () {
    return (
      <div>
        Hello World!
      </div>
      );
  }
});
 
var AnotherComponent = React.createClass({
  mixins: [ExampleMixin],
  render: function () {
    return (
      <div>
        Hello World!
      </div>
      );
  }
});

这样全部组件都有一样的 componentDidMount 和 componentWillUnmount 方法了,保存我们重写的代码。无论如何,你不能 override(覆盖)这些属性,如果这个属性是在mixin里设置的,它在这个组件中是不可覆盖的。

遍历循环

当我们有一个包含对象的数组,如何循环这个数组并渲染每一个对象到列表项中?JSX 允许你在任意 Javascript 文件中使用它,你可以映射这个数组并返回 JSX,然后使用 React 去渲染。
 

var navigationConfig = [
  {
    href: 'http://ryanclark.me',
    text: 'My Website'
  }
];
var Navigation = React.createClass({
  getInitialState: function () {
    return {
      openDropdown: -1
    };
  },
  getDefaultProps: function () {
    return {
      config: []
    }
  },
  propTypes: {
    config: React.PropTypes.array
  },
  render: function () {
    var config = this.props.config;
    var items = config.map(function (item) {
      return (
        <li className="navigation__item">
          <a className="navigation__link" href={ item.href }>
            { item.text }
          </a>
        </li>
        );
    });
    return (
      <div className="navigation">
        { items }
      </div>
      );
  }
});
React.render(<Navigation config={ navigationConfig } />, document.body);

在 JSFilddle 中随意使用 navigationConfigin

导航配置由数组和对象组成,包括一个指向超链接的 href 属性和一个用于显示的 text 属性。当我们映射的时候,它会一个个依次通过这个数组去取得对象。我们可以访问 href 和 text,并在 HTML 中使用。当返回列表时,这个数组里的列表项都将被替换,所以我们把它放入 React 中处理时它将知道怎么去渲染了!

混编

到目前位置,我们已经做到了所有下拉列表的展开。我们需要知道被下来的项目是哪个,我们将使用 .children 属性去遍历我们的 navigationConfig 数组。接下来,我们可以通过循环来操作下拉的子元素条目。

var navigationConfig = [
  {
    href: 'http://ryanclark.me',
    text: 'My Website',
    children: [
      {
        href: 'http://ryanclark.me/how-angularjs-implements-dirty-checking/',
        text: 'Angular Dirty Checking'
      },
      {
        href: 'http://ryanclark.me/getting-started-with-react/',
        text: 'React'
      }
    ]
  }
];
var Navigation = React.createClass({
  getInitialState: function () {
    return {
      openDropdown: -1
    };
  },
  getDefaultProps: function () {
    return {
      config: []
    }
  },
  propTypes: {
    config: React.PropTypes.array
  },
  render: function () {
    var config = this.props.config;
    var items = config.map(function (item) {
      var children, dropdown;
      if (item.children) {
        children = item.children.map(function (child) {
          return (
            <li className="navigation__dropdown__item">
              <a className="navigation__dropdown__link" href={ child.href }>
                { child.text }
              </a>
            </li>
          );
        });
        dropdown = (
          <ul className="navigation__dropdown">
            { children }
          </ul>
        );
      }
      return (
        <li className="navigation__item">
          <a className="navigation__link" href={ item.href }>
            { item.text }
          </a>
          { dropdown }
        </li>
        );
    });
    return (
      <div className="navigation">
        { items }
      </div>
      );
  }
});
React.render(<Navigation config={ navigationConfig } />, document.body);

实例在这里 - 但是我们还是能看见下来条目,尽管我们将 openDropdown 设置成为了 -1 。

我们可以通过在组件中访问 this.state ,来判断下拉是否被打开了,并且,我们可以为其添加一个新的 css 样式 class 来展现鼠标 hover 的效果。
 

var navigationConfig = [
  {
    href: 'http://ryanclark.me',
    text: 'My Website',
    children: [
      {
        href: 'http://ryanclark.me/how-angularjs-implements-dirty-checking/',
        text: 'Angular Dirty Checking'
      },
      {
        href: 'http://ryanclark.me/getting-started-with-react/',
        text: 'React'
      }
    ]
  }
];
var Navigation = React.createClass({
  getInitialState: function () {
    return {
      openDropdown: -1
    };
  },
  getDefaultProps: function () {
    return {
      config: []
    }
  },
  openDropdown: function (id) {
    this.setState({
      openDropdown: id
    });
  },
  closeDropdown: function () {
    this.setState({
      openDropdown: -1
    });
  },
  propTypes: {
    config: React.PropTypes.array
  },
  render: function () {
    var config = this.props.config;
    var items = config.map(function (item, index) {
      var children, dropdown;
      if (item.children) {
        children = item.children.map(function (child) {
          return (
            <li className="navigation__dropdown__item">
              <a className="navigation__dropdown__link" href={ child.href }>
                { child.text }
              </a>
            </li>
          );
        });
        var dropdownClass = 'navigation__dropdown';
        if (this.state.openDropdown === index) {
          dropdownClass += ' navigation__dropdown--open';
        }
        console.log(this.state.openDropdown, index);
        dropdown = (
          <ul className={ dropdownClass }>
            { children }
          </ul>
        );
      }
      return (
        <li className="navigation__item" onMouseOut={ this.closeDropdown } onMouseOver={ this.openDropdown.bind(this, index) }>
          <a className="navigation__link" href={ item.href }>
            { item.text }
          </a>
          { dropdown }
        </li>
        );
    }, this);
    return (
      <div className="navigation">
        { items }
      </div>
      );
  }
});
React.render(<Navigation config={ navigationConfig } />, document.body);

在这里看实例 - 鼠标划过“My Website”,下拉即会展现。


在前面,我已经给每个列表项添加了鼠标事件。如你所见,我用的是 .bind (绑定) 调用,而非其它的方式调用 - 这是因为,当用户的鼠标划出元素区域,我们并不用关注光标在哪里,所有我们需要知晓的是,将下拉关闭掉,所以我们可以将它的值设置成为-1。但是,我们需要知晓的是当用户鼠标划入的时候哪个元素被下拉展开了,所以我们需要知道该参数(元素的索引)。 我们使用绑定的方式去调用而非简单的透过函数(function)去调用是因为我们需要通过 React 去调用。如果我们直接调用,那我们就需要一直调用,而不是在事件中调用他。

现在我们可以添加很多的条目到 navigationConfig 当中,而且我们也可以给他添加样式到下来功能当中。查看实例.

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn