小鱼儿自媒体 發表於 2019-5-6 16:07:00

react基础学习和react服务端渲染框架next.js踩坑

<p><img src="https://user-gold-cdn.xitu.io/2019/5/6/16a8c1e4a9b6f914?w=641&amp;h=273&amp;f=jpeg&amp;s=35844"></p>
<h2 id="说明">说明</h2>
<p>React作为Facebook 内部开发 Instagram 的项目中,是一个用来构建用户界面的优秀 JS 库,于 2013 年 5 月开源。作为前端的三大框架之一,React的应用可以说是非常的广泛。这里讲一个react服务端渲染的框架-next.js踩坑过程。</p>
<h2 id="技术栈">技术栈</h2>
<pre><code>react、next.js、ant design、axios
</code></pre>
<h2 id="大纲">大纲</h2>
<p>按照以下思路来写:</p>
<p><img src="https://user-gold-cdn.xitu.io/2019/5/6/16a8c1e4adfbb311?w=641&amp;h=478&amp;f=jpeg&amp;s=28073"></p>
<h2 id="react基本语法">react基本语法</h2>
<p>react基本语法参照react文档,这里发放一个链接https://doc.react-china.org/。</p>
<h2 id="以下是react重要的部分">以下是React重要的部分</h2>
<ul>
<li>JSX – 允许我们编写类似HTML的语法,转换为JavaScript对象;</li>
<li>虚拟DOM – 实际DOM的JavaScript表示;</li>
<li>React.Component – 创建新组件的方式;</li>
<li>render(方法) – 渲染组件;</li>
<li>ReactDOM.render – 用于将模板转为 HTML 语言,并插入指定的 DOM 节点;</li>
<li>state – 组件的内部数据存储(对象),类型理解vue的data;</li>
<li>constructor(this.state) – 建立组件初始 state(状态) 的方式;</li>
<li>setState – 一种辅助方法,用于更新组件的 state(状态) 并重新渲染,类似微信小程序;</li>
<li>props – 从父组件传递给子组件的数据;</li>
<li>propTypes – 允许您控制传递给子组件的某些 props(属性) 的存在或类型;</li>
<li>defaultProps – 允许您为组件设置默认 props(属性) ;</li>
<li>className - 节点的class,css标记</li>
<li>style jsx样式控制</li>
</ul>
<h2 id="jsx语法">jsx语法</h2>
<p>直接写在 JavaScript 语言之中,不加任何引号,它允许 HTML 与 JavaScript 的混写。</p>
<pre><code>//demo-1.js
import React from 'react';
class Demo1 extends React.Component{
render(){
    const lists = ['我是谁','我来自哪里','我要到哪儿去'];
    return(
      &lt;div className="list"&gt;
      &lt;ul&gt;
          {
            lists.map((list,index)=&gt;{
            return(
                &lt;li key={index}&gt;{list}&lt;/li&gt;
            )
            })
          }
      &lt;/ul&gt;
      &lt;/div&gt;
    )
}
}

export default Demo1;
</code></pre>
<p>上面代码体现了 JSX 的基本语法规则:遇到 HTML 标签(以 &lt; 开头),就用 HTML 规则解析;遇到代码块(以 { 开头),就用 JavaScript 规则解析,其中注意一点react没有vue中v-for遍历,需要自己写遍历。上面代码的运行结果如下:</p>
<p><img src="https://user-gold-cdn.xitu.io/2019/5/6/16a8c1e4aa5a61b0?w=641&amp;h=222&amp;f=jpeg&amp;s=14590"></p>
<h2 id="组件及组件传值">组件及组件传值</h2>
<pre><code>//demo-2.js
import React from 'react';

class MyComponent extends React.Component{
render(){
    return (
      &lt;h1&gt;{this.props.name}&lt;/h1&gt;
    )
}
}

class Demo2 extends React.Component{
render(){
    const lists = ['我是谁','我来自哪里','我要到哪儿去'];
    return(
      &lt;div className="list"&gt;
      &lt;ul&gt;
          {
            lists.map((list,index)=&gt;{
            return(
                &lt;li key={index}&gt;
                  &lt;MyComponent name={list}&gt;&lt;/MyComponent&gt;
                &lt;/li&gt;
            )
            })
          }
      &lt;/ul&gt;
      
      &lt;/div&gt;
    )
}
}

export default Demo2;
</code></pre>
<p>React 允许将代码封装成组件(component),然后像插入普通 HTML 标签一样,在网页中插入这个组件。以上代码中MyComponent即为组件,需要注意的一点组件类的第一个字母必须大写,否则会报错,组件类只能包含一个顶层标签,否则也会报错。vue通过子组件中的props来传递数据,而React则是用this.props.name来传递。下图为渲染页面效果:</p>
<p><img src="https://user-gold-cdn.xitu.io/2019/5/6/16a8c1e4adfd6991?w=641&amp;h=275&amp;f=jpeg&amp;s=19112"></p>
<h2 id="react之ref获取真实的dom节点">React之ref(获取真实的DOM节点)</h2>
<ul>
<li>同vue的ref作用一样,组件并不是真实的DOM节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM。</li>
<li>只有当它插入文档以后,才会变成真实的 DOM 。根据 React的设计,所有的DOM 变动,都先在虚拟 DOM上发生,然后再将实际发生变动的部分,反映在真实 DOM上,这种算法叫做 DOM diff ,它可以极大提高网页的性能表现。</li>
<li>有时需要从组件获取真实 DOM 的节点,这时就要用到 ref 属性。</li>
</ul>
<pre><code>//dem0-3.
import React from 'react';

class Demo3 extends React.Component{
handleClick(){
    this.refs.myTextInput.focus();
}
render(){
    return(
      &lt;div&gt;
      &lt;input type="text" ref="myTextInput" /&gt;
      &lt;br/&gt;
      &lt;input type="button" value="Focus the text input" onClick={this.handleClick.bind(this)} /&gt;
      &lt;/div&gt;
    )
}
}

export default Demo3;

</code></pre>
<p>在这里需要注意的是:</p>
<ul>
<li>React的事件中如果不用剪头函数,那就要用bind来绑定this,否则需要在constructor构造函数里调用this.handleClick = this.handleClick.bind(this),这里建议用onClick={this.handleClick.bind(this)}。</li>
<li>由于 this.refs. 属性获取的是真实 DOM ,所以必须等到虚拟 DOM 插入文档以后,才能使用这个属性,否则会报错。上面代码中,通过为组件指定 Click 事件的回调函数,确保了只有等到真实 DOM 发生 Click 事件之后,才会读取 this.refs. 属性。</li>
</ul>
<p><img src="https://user-gold-cdn.xitu.io/2019/5/6/16a8c1e4b1fd703d?w=640&amp;h=317&amp;f=gif&amp;s=19649"></p>
<h2 id="react之thisstate以及点击事件">React之this.state以及点击事件</h2>
<p>React中的state就相当于vue里的data数据存储,而小程序的this.setData就和React的this.setState类似。</p>
<pre><code>//demo-4.js
import React from 'react';

class Demo4 extends React.Component{
// constructor(props){
//   super(props)
//   this.state = {
//   liked:false
//   }
// }
state = {
    liked: false
}
handleClick(){
    this.setState({liked: !this.state.liked});
}
render(){
    let text = this.state.liked ? '喜欢' : '不喜欢';
    return(
      &lt;div&gt;
      &lt;p onClick={this.handleClick.bind(this)}&gt;
          你 {text} 这个点击.
      &lt;/p&gt;
      &lt;/div&gt;
    )
}
}

export default Demo4;
</code></pre>
<p>上面代码是一个 LikeButton 组件,它的 constructor 方法(或者直接state声明)用于定义初始状态,也就是一个对象,这个对象可以通过 this.state 属性读取。当用户点击组件,导致状态变化,this.setState 方法就修改状态值,每次修改以后,自动调用 this.render 方法,再次渲染组件。</p>
<p>由于 this.props 和 this.state 都用于描述组件的特性,可能会产生混淆。一个简单的区分方法是,this.props 表示那些一旦定义,就不再改变的特性,而 this.state 是会随着用户互动而产生变化的特性。</p>
<p><img src="https://user-gold-cdn.xitu.io/2019/5/6/16a8c1e4ceb97248?w=641&amp;h=318&amp;f=gif&amp;s=5924"></p>
<h2 id="利用input的onchange事件实现简单的双向绑定">利用input的onChange事件实现简单的双向绑定</h2>
<p>vue里面v-model一键实现的事情React没有,我们可以利用input组件的onChange事件来简单实现它,直接上代码。</p>
<pre><code>//Demo5.js
import React from 'react';

class Demo5 extends React.Component{
// constructor(props){
//   super(props)
//   this.state = {
//   text:''
//   }
// }
state = {
    text: ''
}
handleChange(e){
    this.setState(
      {text: e.target.value}
    );
}
render(){
    return(
      &lt;div&gt;
      &lt;p&gt;
          你输入了{this.state.text}
      &lt;/p&gt;
      &lt;input type="text" placeholder="请输入..." onChange={this.handleChange.bind(this)}/&gt;
      &lt;/div&gt;
    )
}
}

export default Demo5;
</code></pre>
<p><img src="https://user-gold-cdn.xitu.io/2019/5/6/16a8c1e52da08130?w=640&amp;h=317&amp;f=gif&amp;s=14903"></p>
<h2 id="组件的生命周期">组件的生命周期</h2>
<p><img src="https://user-gold-cdn.xitu.io/2019/5/6/16a8c1e4d13b6676?w=641&amp;h=662&amp;f=jpeg&amp;s=38584"></p>
<p>一个React组件的生命周期分为三个部分:挂载期(Mounting)、存在更新期(Updating)和销毁时(Unmounting)。</p>
<ul>
<li>
<p>Mounting:已插入真实 DOM</p>
<p>当一个组件实例被创建并且插入到DOM中,以下钩子将被调用 constructor()<br>
继承react的props,和设置state的初始化。</p>
</li>
</ul>
<pre><code>constructor(props) {
super(props); //不能缺少
this.state = {
    color: props.initialColor
};
}
</code></pre>
<ul>
<li>Updating:当组件的props和state发生改变时,重新渲染页面是调用的。</li>
<li>Unmounting:组件从DOM中移动时调用的。</li>
</ul>
<pre><code>React 为每个状态都提供了两种处理函数,will 函数在进入状态之前调用,
did 函数在进入状态之后调用,三种状态共计五种处理函数。
</code></pre>
<ul>
<li>componentWillMount()</li>
<li>componentDidMount()</li>
<li>componentWillUpdate(object nextProps, object nextState)</li>
<li>componentDidUpdate(object prevProps, object prevState)</li>
<li>componentWillUnmount()</li>
</ul>
<h1 id="事件">事件</h1>
<ul>
<li>onClick-点击事件</li>
<li>onSubmit-表单提交事件,可以配合antdesign的表单校验</li>
<li>onChange-input框数据变化,可实现数据的双向绑定(react里没有vue的v-modal)</li>
</ul>
<h1 id="style-jsx">style jsx</h1>
<p>我们可以通过普通样式(className)和行内样式(LineStyle)控制React组件的样式:</p>
<ul>
<li>使用className设置样式(与CSS的选择器相同)</li>
</ul>
<pre><code>import React from 'react';
// import styles from '../../static/css/demo.css'

class Demo6 extends React.Component{
// constructor(props){
//   super(props)
//   this.state = {
//   text:''
//   }
// }
state = {
    text: ''
}
handleChange(e){
    this.setState(
      {text: e.target.value}
    );
}
render(){
    return(
      &lt;div&gt;
      &lt;p className="style"&gt;
          你输入了{this.state.text}
      &lt;/p&gt;
      &lt;input className="input" type="text" placeholder="请输入..." onChange={this.handleChange.bind(this)}/&gt;
      &lt;style jsx&gt;
          {
            ` .style {
                background: #dcdcdc;
                font-size: 20px;
                height:40px;
                line-height:40px;
                padding:0 2%;
            }
            .input{
                width:96%;
                height:40px;
                padding:0 2%;
            }
            `
          }
      &lt;/style&gt;
      &lt;/div&gt;
    )
}
}

export default Demo6;
</code></pre>
<p><img src="https://user-gold-cdn.xitu.io/2019/5/6/16a8c1e4e4e50011?w=641&amp;h=245&amp;f=jpeg&amp;s=18545"></p>
<h2 id="为什么要用next">为什么要用next</h2>
<p>Next.js是一个基于React的一个服务端渲染简约框架。它使用React语法,可以很好的实现代码的模块化,有利于代码的开发和维护。</p>
<ul>
<li>
<p>默认服务端渲染模式,以文件系统为基础的客户端路由,类似nuxt.js;</p>
</li>
<li>
<p>与nuxt.js相同,pages文件目录即路由;</p>
</li>
<li>
<p>以webpack的热替换为基础的开发环境(npm run dev);</p>
</li>
</ul>
<h2 id="next创建的项目目录结构">next创建的项目目录结构</h2>
<p><img src="https://user-gold-cdn.xitu.io/2019/5/6/16a8c1e4d6b48f08?w=480&amp;h=290&amp;f=jpeg&amp;s=10667"></p>
<h2 id="next路由">next路由</h2>
<ul>
<li>默认从 pages 目录下取页面进行渲染返回给前端展示,路由可以使用Link(无刷新页面)或者a标签(刷新页面)</li>
</ul>
<pre><code>//pages/router.js
import Link from 'next/link';

&lt;Link prefetch href='/about?id=11'&gt;
    &lt;a &gt;Link&lt;/a&gt;
&lt;/Link&gt;

&lt;a href="/about?id=11"&gt;Link&lt;/a&gt;
</code></pre>
<ul>
<li>接收参数:getInitialProps方法context参数或者{props.query.id},同时在getInitialProps 里面获取数据,服务端渲染会提前执行这个方法获取数据渲染到模板,这里用到axios库,前后端都可以使用</li>
</ul>
<pre><code>//pages/test.js http://localhost:3000/test
import React from 'react';
import MyHeader from '../components/MyHeader';
import axios from '../lib/axios';
import { bmobConfigDevdev, bmobConfig } from '../lib/config';
import urls from '../lib/urls';

import { Table, Form, Icon, Input, Button } from 'antd';
const FormItem = Form.Item;

const config1 = {
headers: {
    'X-Bmob-Application-Id': bmobConfig.applicationId,
    'X-Bmob-REST-API-Key': bmobConfig.restApiKey,
    'Content-Type': 'application/json'
}
};

const config2 = {
headers: {
    'X-Bmob-Application-Id': bmobConfigDevdev.applicationId,
    'X-Bmob-REST-API-Key': bmobConfigDevdev.restApiKey,
    'Content-Type': 'application/json'
}
};

const myStyle = {
body: {
    width: '1200px',
    margin: '10px auto'
}
}

const columns = [{
title: 'id',
dataIndex: 'objectId',
key: 'objectId',
}, {
title: '姓名',
dataIndex: 'uname',
key: 'uname',
}, {
title: '电话',
dataIndex: 'uphone',
key: 'uphone',
}, {
title: '地址',
dataIndex: 'address',
key: 'address',
}];

const dataSource = [{
key: '1',
name: '胡彦斌',
age: 32,
address: '西湖区湖底公园1号'
}, {
key: '2',
name: '胡彦祖',
age: 42,
address: '西湖区湖底公园1号'
}];

//封装form组件
const MyForm = (props) =&gt; (
&lt;Form layout="inline" onSubmit={this.handleSubmit}&gt;
    &lt;FormItem&gt;
      &lt;Button
      type="primary"
      htmlType="submit"
      &gt;
      增加数据
      &lt;/Button&gt;
    &lt;/FormItem&gt;
&lt;/Form&gt;
)

//声明类
class addDeleteSelectUpdate extends React.Component {

render() {
    return (
      &lt;MyHeader title="用户列表"&gt;
      &lt;div style={myStyle.body}&gt;
          &lt;p className="about"&gt;传递的参数为:{this.props.query}&lt;/p&gt;
          &lt;p className="about"&gt;增加数据&lt;/p&gt;
          &lt;MyForm &gt;&lt;/MyForm&gt;
          &lt;p className="about"&gt;获取数据{this.props.dataToString}&lt;/p&gt;
          &lt;Table dataSource={this.props.data} columns={columns} size="small" /&gt;
          &lt;div className=""&gt;
            &lt;p className="about"&gt;遍历&lt;/p&gt;
            &lt;ul&gt;
            {
                this.props.data.map(els =&gt; {
                  return (
                  &lt;li key={els.objectId}&gt;
                      {els.address}
                  &lt;/li&gt;
                  )
                })
            }
            &lt;/ul&gt;
          &lt;/div&gt;
          &lt;style jsx&gt;
            {`
            .about {color:#666;padding:10px}
            `}
          &lt;/style&gt;
      &lt;/div&gt;
      &lt;/MyHeader&gt;
    )
}
}

/**
* @description 获取初始数据
*/
const isServer = typeof window === 'undefined'

addDeleteSelectUpdate.getInitialProps = async function (context) {
console.log(context.query);
const res = await axios.get(urls.userList, config1)
console.log(res.data);
var query = context.query;
return {
    data: res.data.results,
    dataToString: JSON.stringify(res.data.results),
    query: JSON.stringify(context.query)
}
}

export default addDeleteSelectUpdate;
</code></pre>
<h2 id="数据请求-axios配置">数据请求-axios配置</h2>
<pre><code>Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
</code></pre>
<p>axios具有以下几个特点:</p>
<ul>
<li>既可以在服务端使用又可以在浏览器端使用;</li>
<li>支持 Promise API(async await,解决地狱回调);</li>
<li>拦截请求和响应(401跳转登录等操作);</li>
<li>转换请求数据和响应数据;</li>
<li>自动转换 JSON 数据。</li>
</ul>
<p>以下为axios配置代码:</p>
<pre><code>//lib/axios.js
import axios from 'axios';
import NProgress from 'nprogress';

axios.defaults.headers['Content-Type'] = 'application/x-www-form-urlencoded';
axios.defaults.headers['X-Requested-With'] = 'XMLHttpRequest';
axios.defaults.withCredentials = true;
axios.defaults.timeout = 5000;

//请求之前
axios.interceptors.request.use((config)=&gt;{
if(process.broswer) NProgress.start()//加一个loading
return config;
});

//
axios.interceptors.response.use(
response =&gt; {
    // console.log('-------axiosResponse---------')
    if(process.broswer) NProgress.done()
    return response;
},
error =&gt; {
    // console.log('-------axiosError---------:');
    // console.log(error.response.status==401)
    if(process.broswer) NProgress.done()
    if(error.response&amp;&amp;error.response.status==401){
      window.location.href="/login";//登录失效跳转
    }
    return Promise.reject(error);
}
);

export default axios;

</code></pre>
<p>使用时在页面引入axios配置文件axios.js,具体的axios配置可以参考link-axios文档。</p>
<pre><code>//pages/login.js
import axios from '../lib/axios';
...

axios.post(urls.login,qs.stringify(obj))
.then(res=&gt;{
    console.log(res);
    Router.push('/contract/list');
})
.catch(error=&gt;{
    console.log(error);
})
...
</code></pre>
<h2 id="数据请求-开发环境代理">数据请求-开发环境代理</h2>
<p>前后端分离以后,前端最常见的一个问题就是跨域,之前的om项目基于nuxt可以配置proxy,在next.js里需要用到express和http-proxy-middleware在服务端(node)端做一层转发,代码配置如下:</p>
<pre><code>//server.js
var express =require('express');
var next = require('next');

const devProxy = {
'/api': {
    target: 'http://39.107.58.75:9092/',// 目标服务器 host
    pathRewrite: {'^/api': '/'}, // 重写请求,比如我们源访问的是api/login,那么请求会被解析为/www/login
    changeOrigin: true,// 默认false,是否需要改变原始主机头为目标URL
},
'/jt':{
    target: 'https://api.dededemo.com/plus/weapp.php?action=index&amp;domain=http://23jt.net&amp;skin=weapp&amp;ver=3.0&amp;page=1',// 目标服务器 host
    // pathRewrite: {'^/api': '/'}, // 重写请求,比如我们源访问的是api/login,那么请求会被解析为/www/login
    changeOrigin: true,// 默认false,是否需要改变原始主机头为目标URL
}
}

const port = parseInt(process.env.PORT, 10) || 3000
const env = process.env.NODE_ENV
const dev = env !== 'production'
const app = next({
dir: '.', // base directory where everything is, could move to src later
dev
})

const handle = app.getRequestHandler()

let server
app
.prepare()
.then(() =&gt; {
    server = express()
      
    // Set up the proxy.
    if (dev &amp;&amp; devProxy) {
      const proxyMiddleware = require('http-proxy-middleware')
      Object.keys(devProxy).forEach(function (context) {
      server.use(proxyMiddleware(context, devProxy))
      })
    }

    // Default catch-all handler to allow Next.js to handle all other routes
    server.all('*', (req, res) =&gt; {
      req.headers['Content-Type'] = 'application/x-www-form-urlencoded';
      req.headers['X-Requested-With'] = 'XMLHttpRequest';
      handle(req, res);
      // console.log(req.body);
      }
    )

    server.listen(port, err =&gt; {
      if (err) {
      throw err
      }
      console.log(`&gt; Ready on port ${port} [${env}]`)
    })
})
.catch(err =&gt; {
    console.log('An error occurred, unable to start the server')
    console.log(err)
})

</code></pre>
<p>配置完server.js后还需要在packge.json中配置启动脚本:</p>
<pre><code>"devdev": "cross-env NODE_ENV=development PORT=9527 node server.js",
</code></pre>
<p>注意上线后需要在ngnix里配置反向代理,就不存在跨域的问题。</p>
<h2 id="在nextjs里使用ant-design">在next.js里使用ant design</h2>
<ul>
<li>babel配置</li>
</ul>
<pre><code>//.babelrc
{
"presets": [
    "next/babel"
],
"plugins": [
    [
      "module-resolver", {
      "root": ["."],
      "alias": {
      "styles": "./styles"
      },
      "cwd": "babelrc"
    }],
    ["import", { "libraryName": "antd" }]
],
"ignore": []
}
</code></pre>
<ul>
<li>引用ant design组件,需要用到ant design什么组件按照自己需要引用即可,各个组件使用方法参考ant design的link-官方文档</li>
</ul>
<pre><code>import {Form, Icon,Input, Button, Checkbox, message} from 'antd';
</code></pre>
<h2 id="部署一个nextjs项目">部署一个next.js项目</h2>
<p>Next.js 项目的部署,需要一个 Node.js的服务器。<br>
在开发环境服务器的入口文件就使用上文中提到的 server.js,在 server.js 里添加了针对部署环境的选择,代码如下</p>
<pre><code>const dev = process.env.NODE_ENV !== 'production'
</code></pre>
<p>为了区分部署环境,我们需要在 package.json 中修改 script 属性如下:</p>
<pre><code>"scripts": {
    "dev": "next",
    "devdev": "cross-env NODE_ENV=development PORT=9527 node server.js",
    "build": "next build",
    "start": "next start",
    "generate": "next build &amp;&amp; next export",
    "export-h":"next export -h"
},
</code></pre>
<p>其中,build 命令是用于打包项目,start 命令是用于生产环境部署,devdev 命令是用于本地开发,PORT为启动端口。</p>
<h2 id="注意的几个点">注意的几个点</h2>
<ul>
<li>增加第三方npm包必须加上--save关键字,会pakage.json文件里加入依赖包;</li>
</ul>
<pre><code>//如增加js-md5库
$ npm install js-md5 --save
</code></pre>
<ul>
<li>组件名称必须以大写字母开头,如<strong>MyHeader</strong>;</li>
<li>React的事件中如果不用剪头函数,那就要用bind来绑定this,否则需要在constructor构造函数里调用this.handleClick = this.handleClick.bind(this),这里建议用onClick={this.handleClick.bind(this)}。</li>
</ul>
<h2 id="总结">总结</h2>
<p>只要熟悉React开发,上手一个Next项目很容易,Next 让前端项目开发效率更高。</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li>ant design官方文档:https://ant.design/docs/react/introduce-cn</li>
<li>ant design脚手架市场:http://scaffold.ant.design/#/?tags=antd&amp;tags=react</li>
<li>ant design使用小结:https://www.cnblogs.com/wx1993/p/6511389.html</li>
<li>用Next.js快速上手React服务器渲染 https://segmentfault.com/p/1210000010368182/read</li>
<li>React 服务端渲染框架 Next.js 基于 Gank api 实战 https://orangexc.xyz/2017/10/19/Nextjs-gank/</li>
<li>基于 React 的通用框架 Next.js:服务端 React(axios)<br>
https://www.zcfy.cc/article/react-universal-with-next-js-server-side-react-2158.html</li>
<li>使用next.js完成从开发到部署 https://juejin.im/post/5b08078b51882538ad3f163d</li>
<li>react学习线路图 https://juejin.im/entry/5b49af6d6fb9a04fe25ec224</li>
<li>React使用Next.js作服务器端渲染 https://blog.csdn.net/xcg132566/article/details/78857017</li>
<li>css modules 用法 http://www.ruanyifeng.com/blog/2016/06/css_modules.html</li>
</ul>
<h2 id="更多">更多</h2>
<p>更多前端技术请访问我的博客:https://hurely.github.io</p><br><br>
来源:https://www.cnblogs.com/kung0806/p/10820212.html
頁: [1]
查看完整版本: react基础学习和react服务端渲染框架next.js踩坑