React做爲Facebook 內部開發 Instagram 的項目中,是一個用來構建用戶界面的優秀 JS 庫,於 2013 年 5 月開源。做爲前端的三大框架之一,React的應用能夠說是很是的普遍。這裏講一個react服務端渲染的框架-next.js踩坑過程。php
react、next.js、ant design、axios
按照如下思路來寫:css
react基本語法參照react文檔,這裏發放一個連接https://doc.react-china.org/。html
直接寫在 JavaScript 語言之中,不加任何引號,它容許 HTML 與 JavaScript 的混寫。前端
//demo-1.js import React from 'react'; class Demo1 extends React.Component{ render(){ const lists = ['我是誰','我來自哪裏','我要到哪兒去']; return( <div className="list"> <ul> { lists.map((list,index)=>{ return( <li key={index}>{list}</li> ) }) } </ul> </div> ) } } export default Demo1;
上面代碼體現了 JSX 的基本語法規則:遇到 HTML 標籤(以 < 開頭),就用 HTML 規則解析;遇到代碼塊(以 { 開頭),就用 JavaScript 規則解析,其中注意一點react沒有vue中v-for遍歷,須要本身寫遍歷。上面代碼的運行結果以下:vue
//demo-2.js import React from 'react'; class MyComponent extends React.Component{ render(){ return ( <h1>{this.props.name}</h1> ) } } class Demo2 extends React.Component{ render(){ const lists = ['我是誰','我來自哪裏','我要到哪兒去']; return( <div className="list"> <ul> { lists.map((list,index)=>{ return( <li key={index}> <MyComponent name={list}></MyComponent> </li> ) }) } </ul> </div> ) } } export default Demo2;
React 容許將代碼封裝成組件(component),而後像插入普通 HTML 標籤同樣,在網頁中插入這個組件。以上代碼中MyComponent即爲組件,須要注意的一點組件類的第一個字母必須大寫,不然會報錯,組件類只能包含一個頂層標籤,不然也會報錯。vue經過子組件中的props來傳遞數據,而React則是用this.props.name來傳遞。下圖爲渲染頁面效果:node
//dem0-3. import React from 'react'; class Demo3 extends React.Component{ handleClick(){ this.refs.myTextInput.focus(); } render(){ return( <div> <input type="text" ref="myTextInput" /> <br/> <input type="button" value="Focus the text input" onClick={this.handleClick.bind(this)} /> </div> ) } } export default Demo3;
在這裏須要注意的是:react
React中的state就至關於vue裏的data數據存儲,而小程序的this.setData就和React的this.setState相似。webpack
//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( <div> <p onClick={this.handleClick.bind(this)}> 你 {text} 這個點擊. </p> </div> ) } } export default Demo4;
上面代碼是一個 LikeButton 組件,它的 constructor 方法(或者直接state聲明)用於定義初始狀態,也就是一個對象,這個對象能夠經過 this.state 屬性讀取。當用戶點擊組件,致使狀態變化,this.setState 方法就修改狀態值,每次修改之後,自動調用 this.render 方法,再次渲染組件。ios
因爲 this.props 和 this.state 都用於描述組件的特性,可能會產生混淆。一個簡單的區分方法是,this.props 表示那些一旦定義,就再也不改變的特性,而 this.state 是會隨着用戶互動而產生變化的特性。git
vue裏面v-model一鍵實現的事情React沒有,咱們能夠利用input組件的onChange事件來簡單實現它,直接上代碼。
//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( <div> <p> 你輸入了{this.state.text} </p> <input type="text" placeholder="請輸入..." onChange={this.handleChange.bind(this)}/> </div> ) } } export default Demo5;
一個React組件的生命週期分爲三個部分:掛載期(Mounting)、存在更新期(Updating)和銷燬時(Unmounting)。
Mounting:已插入真實 DOM
當一個組件實例被建立而且插入到DOM中,如下鉤子將被調用 constructor()
繼承react的props,和設置state的初始化。
constructor(props) { super(props); //不能缺乏 this.state = { color: props.initialColor }; }
React 爲每一個狀態都提供了兩種處理函數,will 函數在進入狀態以前調用, did 函數在進入狀態以後調用,三種狀態共計五種處理函數。
咱們能夠經過普通樣式(className)和行內樣式(LineStyle)控制React組件的樣式:
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( <div> <p className="style"> 你輸入了{this.state.text} </p> <input className="input" type="text" placeholder="請輸入..." onChange={this.handleChange.bind(this)}/> <style jsx> { ` .style { background: #dcdcdc; font-size: 20px; height:40px; line-height:40px; padding:0 2%; } .input{ width:96%; height:40px; padding:0 2%; } ` } </style> </div> ) } } export default Demo6;
Next.js是一個基於React的一個服務端渲染簡約框架。它使用React語法,能夠很好的實現代碼的模塊化,有利於代碼的開發和維護。
默認服務端渲染模式,以文件系統爲基礎的客戶端路由,相似nuxt.js;
與nuxt.js相同,pages文件目錄即路由;
以webpack的熱替換爲基礎的開發環境(npm run dev);
//pages/router.js import Link from 'next/link'; <Link prefetch href='/about?id=11'> <a >Link</a> </Link> <a href="/about?id=11">Link</a>
//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) => ( <Form layout="inline" onSubmit={this.handleSubmit}> <FormItem> <Button type="primary" htmlType="submit" > 增長數據 </Button> </FormItem> </Form> ) //聲明類 class addDeleteSelectUpdate extends React.Component { render() { return ( <MyHeader title="用戶列表"> <div style={myStyle.body}> <p className="about">傳遞的參數爲:{this.props.query}</p> <p className="about">增長數據</p> <MyForm ></MyForm> <p className="about">獲取數據{this.props.dataToString}</p> <Table dataSource={this.props.data} columns={columns} size="small" /> <div className=""> <p className="about">遍歷</p> <ul> { this.props.data.map(els => { return ( <li key={els.objectId}> {els.address} </li> ) }) } </ul> </div> <style jsx> {` .about {color:#666;padding:10px} `} </style> </div> </MyHeader> ) } } /** * @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;
Axios 是一個基於 promise 的 HTTP 庫,能夠用在瀏覽器和 node.js 中。
axios具備如下幾個特色:
如下爲axios配置代碼:
//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)=>{ if(process.broswer) NProgress.start()//加一個loading return config; }); // axios.interceptors.response.use( response => { // console.log('-------axiosResponse---------') if(process.broswer) NProgress.done() return response; }, error => { // console.log('-------axiosError---------:'); // console.log(error.response.status==401) if(process.broswer) NProgress.done() if(error.response&&error.response.status==401){ window.location.href="/login";//登陸失效跳轉 } return Promise.reject(error); } ); export default axios;
使用時在頁面引入axios配置文件axios.js,具體的axios配置能夠參考link-axios文檔。
//pages/login.js import axios from '../lib/axios'; ... axios.post(urls.login,qs.stringify(obj)) .then(res=>{ console.log(res); Router.push('/contract/list'); }) .catch(error=>{ console.log(error); }) ...
先後端分離之後,前端最多見的一個問題就是跨域,以前的om項目基於nuxt能夠配置proxy,在next.js裏須要用到express和http-proxy-middleware在服務端(node)端作一層轉發,代碼配置以下:
//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&domain=http://23jt.net&skin=weapp&ver=3.0&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(() => { server = express() // Set up the proxy. if (dev && devProxy) { const proxyMiddleware = require('http-proxy-middleware') Object.keys(devProxy).forEach(function (context) { server.use(proxyMiddleware(context, devProxy[context])) }) } // Default catch-all handler to allow Next.js to handle all other routes server.all('*', (req, res) => { 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 => { if (err) { throw err } console.log(`> Ready on port ${port} [${env}]`) }) }) .catch(err => { console.log('An error occurred, unable to start the server') console.log(err) })
配置完server.js後還須要在packge.json中配置啓動腳本:
"devdev": "cross-env NODE_ENV=development PORT=9527 node server.js",
注意上線後須要在ngnix裏配置反向代理,就不存在跨域的問題。
//.babelrc { "presets": [ "next/babel" ], "plugins": [ [ "module-resolver", { "root": ["."], "alias": { "styles": "./styles" }, "cwd": "babelrc" }], ["import", { "libraryName": "antd" }] ], "ignore": [] }
import {Form, Icon,Input, Button, Checkbox, message} from 'antd';
Next.js 項目的部署,須要一個 Node.js的服務器。
在開發環境服務器的入口文件就使用上文中提到的 server.js,在 server.js 裏添加了針對部署環境的選擇,代碼以下
const dev = process.env.NODE_ENV !== 'production'
爲了區分部署環境,咱們須要在 package.json 中修改 script 屬性以下:
"scripts": { "dev": "next", "devdev": "cross-env NODE_ENV=development PORT=9527 node server.js", "build": "next build", "start": "next start", "generate": "next build && next export", "export-h":"next export -h" },
其中,build 命令是用於打包項目,start 命令是用於生產環境部署,devdev 命令是用於本地開發,PORT爲啓動端口。
//如增長js-md5庫 $ npm install js-md5 --save
只要熟悉React開發,上手一個Next項目很容易,Next 讓前端項目開發效率更高。
更多前端技術請訪問個人博客:https://hurely.github.io