• React教程


    https://www.runoob.com/react/react-tutorial.html

    https://www.w3cschool.cn/react/

    React 教程

    React 是一个用于构建用户界面的 JAVASCRIPT 库。

    React 主要用于构建 UI,很多人认为 React 是 MVC 中的 V(视图)。

    React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站,并于 2013 年 5 月开源。

    React 拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它。

    React 特点

    • 1.声明式设计 −React采用声明范式,可以轻松描述应用。

    • 2.高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。

    • 3.灵活 −React可以与已知的库或框架很好地配合。

    • 4.JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。

    • 5.组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。

    • 6.单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。

    React 安装

    React 可以直接下载使用,下载包中也提供了很多学习的实例。

    本教程使用了 React 的版本为 16.4.0,你可以在官网 https://reactjs.org/ 下载最新版。

    你也可以直接使用 Staticfile CDN 的 React CDN 库,地址如下:

    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 --><script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>

    官方提供的 CDN 地址:

    <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 --> <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>

    实例中我们引入了三个库: react.development.min.js 、react-dom.development.min.js 和 babel.min.js:

    • react.min.js - React 的核心库
    • react-dom.min.js - 提供与 DOM 相关的功能
    • babel.min.js - Babel 可以将 ES6 代码转为 ES5 代码,这样我们就能在目前不支持 ES6 浏览器上执行 React 代码。Babel 内嵌了对 JSX 的支持。通过将 Babel 和 babel-sublime 包(package)一同使用可以让源码的语法渲染上升到一个全新的水平。

    通过 npm 使用 React

    使用 create-react-app 快速构建 React 开发环境

    create-react-app 是来自于 Facebook,通过该命令我们无需配置就能快速构建 React 开发环境。

    create-react-app 自动创建的项目是基于 Webpack + ES6 。

    执行以下命令创建项目:

    $ cnpm install -g create-react-app
    $ create-react-app my-app
    $ cd my-app/
    $ npm start

    http://localhost:3000/ 

    1、React 元素渲染

    元素是构成 React 应用的最小单位,它用于描述屏幕上输出的内容。

    const element = <h1>Hello, world!</h1>;

    与浏览器的 DOM 元素不同,React 当中的元素事实上是普通的对象,React DOM 可以确保 浏览器 DOM 的数据内容与 React 元素保持一致。

    将元素渲染到 DOM 中

    首先我们在一个 HTML 页面中添加一个 id="example" 的 <div>:

    const element = <h1>Hello, world!</h1>;
    ReactDOM.render(
        element,
        document.getElementById('example')
    );
    更新元素渲染
    function tick() {
      const element = (
        <div>
          <h1>Hello, world!</h1>
          <h2>现在是 {new Date().toLocaleTimeString()}.</h2>
        </div>
      );
      ReactDOM.render(
        element,
        document.getElementById('example')
      );
    }
     
    setInterval(tick, 1000);
    

    2、React JSX

    React 使用 JSX 来替代常规的 JavaScript。

    JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。

    我们不需要一定使用 JSX,但它有以下优点:

    • JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
    • 它是类型安全的,在编译过程中就能发现错误。
    • 使用 JSX 编写模板更加简单快速。
    ReactDOM.render(
        <div>
        <h1>菜鸟教程</h1>
        <h2>欢迎学习 React</h2>
        <p data-myattribute = "somevalue">这是一个很不错的 JavaScript 库!</p>
        </div>
        ,
        document.getElementById('example')
    );

    3、React 组件

    接下来我们封装一个输出 "Hello World!" 的组件,组件名为 HelloMessage:

    1、我们可以使用函数定义了一个组件:
    2、const element = <HelloMessage /> 为用户自定义的组件。
    function HelloMessage(props) {
        return <h1>Hello World!</h1>;
    }
     
    const element = <HelloMessage />;
     
    ReactDOM.render(
        element,
        document.getElementById('example')
    );
    或你也可以使用 ES6 class 来定义一个组件:
    class Welcome extends React.Component {
      render() {
        return <h1>Hello World!</h1>;
      }
    }
    组件使用
    function HelloMessage(props) {
        return <h1>Hello {props.name}!</h1>;
    }
     
    const element = <HelloMessage name="Runoob"/>;
     
    ReactDOM.render(
        element,
        document.getElementById('example')
    );
    复合组件
    function Name(props) {
        return <h1>网站名称:{props.name}</h1>;
    }
    function Url(props) {
        return <h1>网站地址:{props.url}</h1>;
    }
    function Nickname(props) {
        return <h1>网站小名:{props.nickname}</h1>;
    }
    function App() {
        return (
        <div>
            <Name name="菜鸟教程" />
            <Url url="http://www.runoob.com" />
            <Nickname nickname="Runoob" />
        </div>
        );
    }
     
    ReactDOM.render(
         <App />,
        document.getElementById('example')
    );
    

    4、React State(状态)

    React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。

    React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。

    以下实例创建一个名称扩展为 React.Component 的 ES6 类,在 render() 方法中使用 this.state 来修改当前的时间。

    添加一个类构造函数来初始化状态 this.state,类组件应始终使用 props 调用基础构造函数。

    class Clock extends React.Component {
      constructor(props) {
        super(props);
        this.state = {date: new Date()};
      }
     
      render() {
        return (
          <div>
            <h1>Hello, world!</h1>
            <h2>现在是 {this.state.date.toLocaleTimeString()}.</h2>
          </div>
        );
      }
    }
     
    ReactDOM.render(
      <Clock />,
      document.getElementById('example')
    );
    
    function FormattedDate(props) {
      return <h2>现在是 {props.date.toLocaleTimeString()}.</h2>;
    }
     
    class Clock extends React.Component {
      constructor(props) {
        super(props);
        this.state = {date: new Date()};
      }
     
      componentDidMount() {
        this.timerID = setInterval(
          () => this.tick(),
          1000
        );
      }
     
      componentWillUnmount() {
        clearInterval(this.timerID);
      }
     
      tick() {
        this.setState({
          date: new Date()
        });
      }
     
      render() {
        return (
          <div>
            <h1>Hello, world!</h1>
            <FormattedDate date={this.state.date} />
          </div>
        );
      }
    }
     
    ReactDOM.render(
      <Clock />,
      document.getElementById('example')
    );
    

    实例解析:

    componentDidMount() 与 componentWillUnmount() 方法被称作生命周期钩子。

    在组件输出到 DOM 后会执行 componentDidMount() 钩子,我们就可以在这个钩子上设置一个定时器。

    this.timerID 为定时器的 ID,我们可以在 componentWillUnmount() 钩子中卸载定时器。

    5、React Props

    state 和 props 主要的区别在于 props 是不可变的,而 state 可根据与用户交互来改变。这就是为什么有些容器组件需要定义 state 来更新和修改数据。 子组件只能通过 props 来传递数据。

    State 和 Props

    以下实例演示了如何在应用中组合使用 state 和 props 。我们可以在父组件中设置 state, 并通过在子组件上使用 props 将其传递到子组件上。在 render 函数中, 我们设置 name 和 site 来获取父组件传递过来的数据。

    class WebSite extends React.Component {
      constructor() {
          super();
     
          this.state = {
            name: "菜鸟教程",
            site: "https://www.runoob.com"
          }
        }
      render() {
        return (
          <div>
            <Name name={this.state.name} />
            <Link site={this.state.site} />
          </div>
        );
      }
    }
    class Name extends React.Component {
      render() {
        return (
          <h1>{this.props.name}</h1>
        );
      }
    }
    class Link extends React.Component {
      render() {
        return (
          <a href={this.props.site}>
            {this.props.site}
          </a>
        );
      }
    }
    ReactDOM.render(
      <WebSite />,
      document.getElementById('example')
    );
    

    6、React 事件处理

    React 元素的事件处理和 DOM 元素类似。但是有一点语法上的不同:

    • React 事件绑定属性的命名采用驼峰式写法,而不是小写。
    • 如果采用 JSX 的语法你需要传入一个函数作为事件处理函数,而不是一个字符串(DOM 元素的写法)
    HTML 通常写法是:
    <button onclick="activateLasers()">
      激活按钮
    </button>
    React 中写法为:
    <button onClick={activateLasers}>
      激活按钮
    </button>
    function ActionLink() {
      function handleClick(e) {
        e.preventDefault();
        console.log('链接被点击');
      }
     
      return (
        <a href="#" onClick={handleClick}>
          点我
        </a>
      );
    }
    class Toggle extends React.Component {
      constructor(props) {
        super(props);
        this.state = {isToggleOn: true};
     
        // 这边绑定是必要的,这样 `this` 才能在回调函数中使用
        this.handleClick = this.handleClick.bind(this);
      }
     
      handleClick() {
        this.setState(prevState => ({
          isToggleOn: !prevState.isToggleOn
        }));
      }
     
      render() {
        return (
          <button onClick={this.handleClick}>
            {this.state.isToggleOn ? 'ON' : 'OFF'}
          </button>
        );
      }
    }
     
    ReactDOM.render(
      <Toggle />,
      document.getElementById('example')
    );
    

    向事件处理程序传递参数

    通常我们会为事件处理程序传递额外的参数。例如,若是 id 是你要删除那一行的 id,以下两种方式都可以向事件处理程序传递参数:

    <button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
    <button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

    7、React 条件渲染

    class LoginControl extends React.Component {
      constructor(props) {
        super(props);
        this.handleLoginClick = this.handleLoginClick.bind(this);
        this.handleLogoutClick = this.handleLogoutClick.bind(this);
        this.state = {isLoggedIn: false};
      }
     
      handleLoginClick() {
        this.setState({isLoggedIn: true});
      }
     
      handleLogoutClick() {
        this.setState({isLoggedIn: false});
      }
     
      render() {
        const isLoggedIn = this.state.isLoggedIn;
     
        let button = null;
        if (isLoggedIn) {
          button = <LogoutButton onClick={this.handleLogoutClick} />;
        } else {
          button = <LoginButton onClick={this.handleLoginClick} />;
        }
     
        return (
          <div>
            <Greeting isLoggedIn={isLoggedIn} />
            {button}
          </div>
        );
      }
    }
     
    ReactDOM.render(
      <LoginControl />,
      document.getElementById('example')
    );
    

    元素变量

    你可以使用变量来储存元素。它可以帮助你有条件的渲染组件的一部分,而输出的其他部分不会更改。

    在下面的例子中,我们将要创建一个名为 LoginControl 的有状态的组件。

    它会根据当前的状态来渲染 <LoginButton /> 或 <LogoutButton />,它也将渲染前面例子中的 <Greeting />。

    与运算符 &&

    你可以通过用花括号包裹代码在 JSX 中嵌入任何表达式 ,也包括 JavaScript 的逻辑与 &&,它可以方便地条件渲染一个元素。

    三目运算符

    条件渲染的另一种方法是使用 JavaScript 的条件运算符:

    condition ? true : false

    阻止组件渲染

    在极少数情况下,你可能希望隐藏组件,即使它被其他组件渲染。让 render 方法返回 null 而不是它的渲染结果即可实现。

    在下面的例子中,<WarningBanner /> 根据属性 warn 的值条件渲染。如果 warn 的值是 false,则组件不会渲染:

    8、React 列表 & Keys

    function NumberList(props) {
      const numbers = props.numbers;
      const listItems = numbers.map((number) =>
        <li key={number.toString()}>
          {number}
        </li>
      );
      return (
        <ul>{listItems}</ul>
      );
    }
     
    const numbers = [1, 2, 3, 4, 5];
    ReactDOM.render(
      <NumberList numbers={numbers} />,
      document.getElementById('example')
    );
    

    Keys

    Keys 可以在 DOM 中的某些元素被增加或删除的时候帮助 React 识别哪些元素发生了变化。因此你应当给数组中的每一个元素赋予一个确定的标识。

    用keys提取组件

    元素的 key 只有在它和它的兄弟节点对比时才有意义。

    比方说,如果你提取出一个 ListItem 组件,你应该把 key 保存在数组中的这个 <ListItem /> 元素上,而不是放在 ListItem 组件中的 <li> 元素上。

    元素的 key 在他的兄弟元素之间应该唯一

    数组元素中使用的 key 在其兄弟之间应该是独一无二的。然而,它们不需要是全局唯一的。当我们生成两个不同的数组时,我们可以使用相同的键。

    function ListItem(props) {
      // 对啦!这里不需要指定key:
      return <li>{props.value}</li>;
    }
     
    function NumberList(props) {
      const numbers = props.numbers;
      const listItems = numbers.map((number) =>
        // 又对啦!key应该在数组的上下文中被指定
        <ListItem key={number.toString()}
                  value={number} />
     
      );
      return (
        <ul>
          {listItems}
        </ul>
      );
    }
     
    const numbers = [1, 2, 3, 4, 5];
    ReactDOM.render(
      <NumberList numbers={numbers} />,
      document.getElementById('example')
    );
    

    9、React 组件 API

    我们将讨论 React 组件 API。我们将讲解以下7个方法:

    • 设置状态:setState
    • 替换状态:replaceState
    • 设置属性:setProps
    • 替换属性:replaceProps
    • 强制更新:forceUpdate
    • 获取DOM节点:findDOMNode
    • 判断组件挂载状态:isMounted
    class Counter extends React.Component{
      constructor(props) {
          super(props);
          this.state = {clickCount: 0};
          this.handleClick = this.handleClick.bind(this);
      }
      
      handleClick() {
        this.setState(function(state) {
          return {clickCount: state.clickCount + 1};
        });
      }
      render () {
        return (<h2 onClick={this.handleClick}>点我!点击次数为: {this.state.clickCount}</h2>);
      }
    }
    ReactDOM.render(
      <Counter />,
      document.getElementById('example')
    );
    

    替换状态:replaceState

    replaceState(object nextState[, function callback])
    • nextState,将要设置的新状态,该状态会替换当前的state
    • callback,可选参数,回调函数。该函数会在replaceState设置成功,且组件重新渲染后调用。

    replaceState()方法与setState()类似,但是方法只会保留nextState中状态,原state不在nextState中的状态都会被删除。


    设置属性:setProps

    setProps(object nextProps[, function callback])
    • nextProps,将要设置的新属性,该状态会和当前的props合并
    • callback,可选参数,回调函数。该函数会在setProps设置成功,且组件重新渲染后调用。

    设置组件属性,并重新渲染组件。

    props相当于组件的数据流,它总是会从父组件向下传递至所有的子组件中。当和一个外部的JavaScript应用集成时,我们可能会需要向组件传递数据或通知React.render()组件需要重新渲染,可以使用setProps()

    更新组件,我可以在节点上再次调用React.render(),也可以通过setProps()方法改变组件属性,触发组件重新渲染。

    10、React 组件生命周期

    组件的生命周期可分成三个状态:

    • Mounting(挂载):已插入真实 DOM
    • Updating(更新):正在被重新渲染
    • Unmounting(卸载):已移出真实 DOM

    挂载

    当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:

    • constructor(): 在 React 组件挂载之前,会调用它的构造函数。
    • getDerivedStateFromProps(): 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。
    • render(): render() 方法是 class 组件中唯一必须实现的方法。
    • componentDidMount(): 在组件挂载后(插入 DOM 树中)立即调用。

    render() 方法是 class 组件中唯一必须实现的方法,其他方法可以根据自己的需要来实现。

    这些方法的详细说明,可以参考官方文档


    更新

    每当组件的 state 或 props 发生变化时,组件就会更新。

    当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:

    • getDerivedStateFromProps(): 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。根据 shouldComponentUpdate() 的返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响。
    • shouldComponentUpdate():当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。
    • render(): render() 方法是 class 组件中唯一必须实现的方法。
    • getSnapshotBeforeUpdate(): 在最近一次渲染输出(提交到 DOM 节点)之前调用。
    • componentDidUpdate(): 在更新后会被立即调用。

    render() 方法是 class 组件中唯一必须实现的方法,其他方法可以根据自己的需要来实现。

    这些方法的详细说明,可以参考官方文档


    卸载

    当组件从 DOM 中移除时会调用如下方法:

    class Clock extends React.Component {
      constructor(props) {
        super(props);
        this.state = {date: new Date()};
      }
     
      componentDidMount() {
        this.timerID = setInterval(
          () => this.tick(),
          1000
        );
      }
     
      componentWillUnmount() {
        clearInterval(this.timerID);
      }
     
      tick() {
        this.setState({
          date: new Date()
        });
      }
     
      render() {
        return (
          <div>
            <h1>Hello, Runoob!</h1>
            <h2>现在时间是:{this.state.date.toLocaleTimeString()}.</h2>
          </div>
        );
      }
    }
     
    ReactDOM.render(
      <Clock />,
      document.getElementById('root')
    );
    

    11、React AJAX

    React 组件的数据可以通过 componentDidMount 方法中的 Ajax 来获取,当从服务端获取数据时可以将数据存储在 state 中,再用 this.setState 方法重新渲染 UI。

    当使用异步加载数据时,在组件卸载前使用 componentWillUnmount 来取消未完成的请求。

    以下实例演示了获取 Github 用户最新 gist 共享描述:

    class UserGist extends React.Component {
      constructor(props) {
          super(props);
          this.state = {username: '', lastGistUrl: ''};
      }
     
     
      componentDidMount() {
        this.serverRequest = $.get(this.props.source, function (result) {
          var lastGist = result[0];
          this.setState({
            username: lastGist.owner.login,
            lastGistUrl: lastGist.html_url
          });
        }.bind(this));
      }
     
      componentWillUnmount() {
        this.serverRequest.abort();
      }
     
      render() {
        return (
          <div>
            {this.state.username} 用户最新的 Gist 共享地址:
            <a href={this.state.lastGistUrl}>{this.state.lastGistUrl}</a>
          </div>
        );
      }
    }
     
    ReactDOM.render(
      <UserGist source="https://api.github.com/users/octocat/gists" />,
      document.getElementById('example')
    );
    

    12、React 表单与事件

    在 HTML 当中,像 <input>, <textarea>, 和 <select> 这类表单元素会维持自身状态,并根据用户输入进行更新。但在React中,可变的状态通常保存在组件的状态属性中,并且只能用 setState() 方法进行更新。

    在实例中我们设置了输入框 input 值 value = {this.state.data}。在输入框值发生变化时我们可以更新 state。我们可以使用 onChange 事件来监听 input 的变化,并修改 state。
    class Content extends React.Component {
      render() {
        return  <div>
                <input type="text" value={this.props.myDataProp} onChange={this.props.updateStateProp} /> 
                <h4>{this.props.myDataProp}</h4>
                </div>;
      }
    }
    class HelloMessage extends React.Component {
      constructor(props) {
          super(props);
          this.state = {value: 'Hello Runoob!'};
          this.handleChange = this.handleChange.bind(this);
      }
     
      handleChange(event) {
        this.setState({value: event.target.value});
      }
      render() {
        var value = this.state.value;
        return <div>
                <Content myDataProp = {value} 
                  updateStateProp = {this.handleChange}></Content>
               </div>;
      }
    }
    ReactDOM.render(
      <HelloMessage />,
      document.getElementById('example')
    );
    

    Select 下拉菜单

    class FlavorForm extends React.Component {
      constructor(props) {
        super(props);
        this.state = {value: 'coconut'};
     
        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
      }
     
      handleChange(event) {
        this.setState({value: event.target.value});
      }
     
      handleSubmit(event) {
        alert('Your favorite flavor is: ' + this.state.value);
        event.preventDefault();
      }
     
      render() {
        return (
          <form onSubmit={this.handleSubmit}>
            <label>
              选择您最喜欢的网站
              <select value={this.state.value} onChange={this.handleChange}>
                <option value="gg">Google</option>
                <option value="rn">Runoob</option>
                <option value="tb">Taobao</option>
                <option value="fb">Facebook</option>
              </select>
            </label>
            <input type="submit" value="提交" />
          </form>
        );
      }
    }
     
    ReactDOM.render(
      <FlavorForm />,
      document.getElementById('example')
    );
    

    多个表单

    class Reservation extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          isGoing: true,
          numberOfGuests: 2
        };
     
        this.handleInputChange = this.handleInputChange.bind(this);
      }
     
      handleInputChange(event) {
        const target = event.target;
        const value = target.type === 'checkbox' ? target.checked : target.value;
        const name = target.name;
     
        this.setState({
          [name]: value
        });
      }
     
      render() {
        return (
          <form>
            <label>
              是否离开:
              <input
                name="isGoing"
                type="checkbox"
                checked={this.state.isGoing}
                onChange={this.handleInputChange} />
            </label>
            <br />
            <label>
              访客数:
              <input
                name="numberOfGuests"
                type="number"
                value={this.state.numberOfGuests}
                onChange={this.handleInputChange} />
            </label>
          </form>
        );
      }
    }
    
    事件
    class HelloMessage extends React.Component {
      constructor(props) {
          super(props);
          this.state = {value: 'Hello Runoob!'};
          this.handleChange = this.handleChange.bind(this);
      }
      
      handleChange(event) {
        this.setState({value: '菜鸟教程'})
      }
      render() {
        var value = this.state.value;
        return <div>
                <button onClick={this.handleChange}>点我</button>
                <h4>{value}</h4>
               </div>;
      }
    }
    ReactDOM.render(
      <HelloMessage />,
      document.getElementById('example')
    );
    

    14、React Refs

    React 支持一种非常特殊的属性 Ref ,你可以用来绑定到 render() 输出的任何组件上。

    这个特殊的属性允许你引用 render() 返回的相应的支撑实例( backing instance )。这样就可以确保在任何时间总是拿到正确的实例。

    使用方法

    绑定一个 ref 属性到 render 的返回值上:

    <input ref="myInput" />

    在其它代码中,通过 this.refs 获取支撑实例:

    var input = this.refs.myInput;
    var inputValue = input.value;
    var inputRect = input.getBoundingClientRect();
    class MyComponent extends React.Component {
      handleClick() {
        // 使用原生的 DOM API 获取焦点
        this.refs.myInput.focus();
      }
      render() {
        //  当组件插入到 DOM 后,ref 属性添加一个组件的引用于到 this.refs
        return (
          <div>
            <input type="text" ref="myInput" />
            <input
              type="button"
              value="点我输入框获取焦点"
              onClick={this.handleClick.bind(this)}
            />
          </div>
        );
      }
    }
     
    ReactDOM.render(
      <MyComponent />,
      document.getElementById('example')
    );
    
     
     
     
     
     
  • 相关阅读:
    .net的一致性哈希实现
    一次基于etcd的分布式锁自动延时失败问题的排查
    一次kubernetes资源文件创建失败的排查
    去除右键菜单中的图形属性
    三款实用的视频格式转换工具
    使用iframe设置frameset的高度
    IIS中asp网站播放flv视频技术
    Joomla3.1.1在64位win7下安装
    64位win7旗舰版搭建apache+php+mysql开发环境[转]
    Windows下实战Apache+PHP [转]
  • 原文地址:https://www.cnblogs.com/hanease/p/16260956.html
Copyright © 2020-2023  润新知