*在本博客后面会贴上代码案例,不想看步骤的大佬可以直接拉到最下面,萌新博客见谅
cnpm i -g create-next-app //全局安装next脚手架,使用npx的用户可以跳过这步骤
create-next-app --example with-ant-design with-redux-app // 引入ant-design的next项目
//npx安装
npx create-next-app --example with-ant-design with-redux-app
cnpm i @zeit/next-sass node-sass null-loader --save //@zeit/next-sass依赖于@zeit/next-css 所以无需引入css
cnpm i next-compose-plugins --save //结合sass 、 less 和 css
const withCss = require('@zeit/next-css')
const withSass = require('@zeit/next-sass');
const withPlugins = require("next-compose-plugins/lib");//结合sass css
module.exports = withPlugins([withSass,withCss],{//[withSass,withCss]可以引入各种依赖,less也可以
webpack: (config, { isServer }) => {
if (isServer) {
const antStyles = /antd\/.*?\/style\/css.*?/
const origExternals = [...config.externals]
config.externals = [
(context, request, callback) => {
if (request.match(antStyles)) return callback()
if (typeof origExternals[0] === 'function') {
origExternals[0](context, request, callback)
} else {
callback()
}
},
...(typeof origExternals[0] === 'function' ? [] : origExternals),
]
config.module.rules.unshift({
test: antStyles,
use: 'null-loader',
});
}
return config
},
})
cnpm i react-redux redux redux-devtools-extension redux-thunk --save//引入依赖
import React from 'react'
import { initializeStore } from '../redux/store'//引入初始化的store
const isServer = typeof window === 'undefined';//判断一下window在不在
const __NEXT_REDUX_STORE__ = '__NEXT_REDUX_STORE__'//定一个常量用来对象存储的key名
function getOrCreateStore (initialState) {
// Always make a new store if server, otherwise state is shared between requests
if (isServer) {//当window不存在的时候 直接返回初始化的整个store 就是在服务器的时候
return initializeStore(initialState)
}
// Create store if unavailable on the client and set it on the window object
if (!window[__NEXT_REDUX_STORE__]) {//这个索引的对象不存在 客户端的时候,注入一个属性
window[__NEXT_REDUX_STORE__] = initializeStore(initialState)//就初始化整个store给它
}
return window[__NEXT_REDUX_STORE__]//最终返回这个对象的key为__NEXT_REDUX_STORE__的值,也就是初始化的整个store
}
export default App => {//App为传入的组件
return class AppWithRedux extends React.Component {
static async getInitialProps (appContext) {
// Get or Create the store with `undefined` as initialState
// This allows you to set a custom default initialState
const reduxStore = getOrCreateStore();//获取初始化的整个store
// Provide the store to getInitialProps of pages
appContext.ctx.reduxStore = reduxStore//然后赋值给整个上下文
let appProps = {}
if (typeof App.getInitialProps === 'function') {
appProps = await App.getInitialProps(appContext)
// console.log(appProps);
}
return {
...appProps,
initialReduxState: reduxStore.getState()//获取初始化的state值
}
}
constructor (props) {
super(props)
// console.log(props);
this.reduxStore = getOrCreateStore(props.initialReduxState);//获取整个store
}
render () {
return <App {...this.props} reduxStore={this.reduxStore} />
}
}
}
import App, { Container } from 'next/app'
import React from 'react'
import withReduxStore from '../lib/with-redux-store'//引入高阶组件
import { provider } from 'react-redux';//引入并使用
class MyApp extends App {
render () {
const { Component, pageProps, reduxStore } = this.props
//reduxStore:就是创建的redux的store
return (
<Container>
<Provider store={reduxStore}>
<Component {...pageProps} />
</Provider>
</Container>
)
}
}
export default withReduxStore(MyApp);
import { createStore,combineReducers,applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
// import createSagaMiddleware from 'redux-saga';//选这个 或者上面的那个redux-thunk 这个需要安装yarn add next-redux-saga redux-saga --save
// initialState
const exampleInitialState = {
todos:['hello next-redux']
}
// action-type
const actionTypes = {
ADD_TODO:'ADD_TODO'
}
// REDUCERS(reducer)
const reducer = (state = exampleInitialState, action) => {
switch (action.type) {
case actionTypes.ADD_TODO:
const todos = [...state.todos];
todos.push(action.todo);
return Object.assign({},state,{
todos
});
default:
return state
}
}
// const reducers = combineReducers({
// reducer
// });
// actions
export const addTodo = todo => ({type:actionTypes.ADD_TODO,todo});
export const asyncAddToDo = (todo) => dispatch => {
return setTimeout(()=>dispatch(addTodo(todo)),2000);
}
// store 高阶组件调用写法,最好不要修改,修改的话就需要修改高阶组件的配置
export function initializeStore (initialState = exampleInitialState) {
return createStore(
reducer,
initialState,
applyMiddleware(thunk)
)
}
cnpm i @babel/plugin-proposal-decorators --save
{
"presets": ["next/babel"],//next/babel next内置的模块,脚手架自带无需安装
"plugins": [
// @babel/plugin-proposal-decorators 这个插件,同时启用装饰器
["@babel/plugin-proposal-decorators", {
"legacy": true
}]
]
}
cnpm i @zeit/next-less less -S //引入less的依赖
cnpm i babel-plugin-import -S //配置babel按需加载
{
"presets": ["next/babel"],//next/babel next内置的模块,脚手架自带无需安装
"plugins": [
// @babel/plugin-proposal-decorators 这个插件,同时启用装饰器
["@babel/plugin-proposal-decorators", {
"legacy": true
}],
[
"import",
{
"libraryName": "antd",
"style": "css"//这个改成ture,就可以使用less编译了,同时也能更改主题
}
]
]
}
cnpm i typescript -g //(本地没有的大佬先执行这条命令)
cnpm i @zeit/next-typescript -D//引入next官方推荐的方式
{
"presets": ["next/babel","@zeit/next-typescript/babel"],//@zeit/next-typescript 引入ts, yarn add @zeit/next-typescript --save
"plugins": [
// 1.可以使用装饰器decorator @babel/plugin-proposal-decorators 这个插件
["@babel/plugin-proposal-decorators", {
"legacy": true
}],
//yarn add @zeit/next-css @zeit/next-less less
// yarn add babel-plugin-import 这些安装之后可以配置less配置+按需加载
// 按需加载并且可以使用less的配置
[
"import",
{
"libraryName": "antd",
"style": "css"
}
]
]
}
const withCss = require('@zeit/next-css')
const withSass = require('@zeit/next-sass');
// const withLess = require('@zeit/next-less');//需要使用less不实用scss的大佬,把withSass替换成withSass即可,如果都需要就都引入
const withTypescript = require('@zeit/next-typescript');//引入typescript,让next解析
const withPlugins = require("next-compose-plugins/lib");//结合sass css
module.exports = withPlugins([withSass,withCss,withTypescript],{
webpack: (config, { isServer }) => {
if (isServer) {
const antStyles = /antd\/.*?\/style\/css.*?/
const origExternals = [...config.externals]
config.externals = [
(context, request, callback) => {
if (request.match(antStyles)) return callback()
if (typeof origExternals[0] === 'function') {
origExternals[0](context, request, callback)
} else {
callback()
}
},
...(typeof origExternals[0] === 'function' ? [] : origExternals),
]
config.module.rules.unshift({
test: antStyles,
use: 'null-loader',
});
}
return config
},
})
{
"compilerOptions": {
"allowJs": true,
"experimentalDecorators":true,
"allowSyntheticDefaultImports": true,
"jsx": "preserve",
"lib": ["dom", "es2019"],
"module": "esnext",
"moduleResolution": "node",
"noEmit": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"preserveConstEnums": true,
"removeComments": false,
"skipLibCheck": true,
"sourceMap": true,
"strict": false,
"target": "esnext"
}
}
cnpm i @types/node --save
*这样我们就能使用我们的TypeScript版本的next了
如果不需要使用TypeScript的可以省略12以下的配置
{
"name": "with-ant-design",
"version": "1.0.0",
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},
"dependencies": {
"@babel/plugin-proposal-decorators": "^7.4.4",
"@types/node": "^12.7.1",
"@zeit/next-css": "^1.0.1",
"@zeit/next-sass": "^1.0.1",
"@zeit/next-typescript": "^1.1.1",
"antd": "^3.9.2",
"babel-plugin-import": "^1.9.1",
"next": "^8.1.0",
"next-compose-plugins": "^2.2.0",
"node-sass": "^4.12.0",
"null-loader": "2.0.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-redux": "^7.1.0",
"redux": "^4.0.1",
"redux-devtools-extension": "^2.13.8",
"redux-thunk": "^2.3.0"
},
"license": "ISC"
}
import React, { PureComponent } from 'react';
import { Button, Input, List } from 'antd';
import { connect } from 'react-redux'
import { addTodo,asyncAddToDo } from '../redux/store';
const Item = List.Item;
interface TodoProps{
todos?:Array<string>;
addTodo?:(todo:string)=>void;
asyncAddToDo?:(todo:string)=>void;
}
const mapStateToProps = state => {
return ({ todos: state.todos });
};
const mapDispatchToProps = { addTodo,asyncAddToDo };
@connect(
mapStateToProps,
mapDispatchToProps
)
class Todo extends PureComponent<TodoProps,{todo:string}> {
constructor(props:TodoProps) {
super(props);
this.state = {
todo: ""
}
}
changeTodo = el => {
this.setState({
todo: el.target.value
});
}
//1.2这是给父组件调用的函数
childMethod = () => {
console.log(this.state.todo);
return this.state.todo;
}
render() {
const { todos, addTodo,asyncAddToDo } = this.props;
const { todo } = this.state;
const liList = todos.map((todo, index) => (<Item key={index}>{todo}</Item>));
return (
<div>
<div>
<Input type="text" defaultValue={todo} onChange={this.changeTodo} />
<Button onClick={addTodo.bind(this,todo)}>添加</Button>
<Button onClick={asyncAddToDo.bind(this,todo)}>添加</Button>
</div>
<List>
{liList}
</List>
</div>
);
}
}
export default Todo;
import '../assets/index.scss';
import Todo from '../components/todo';
function Index(): JSX.Element {
return (
<Todo />
);
}
export default Index;
import App, { Container } from 'next/app';
import React from 'react';
import withReduxStore from '../lib/with-redux-store';//高阶组件
import { Provider } from 'react-redux';
@withReduxStore//直接使用装饰器的写法,注入高阶组件
class MyApp extends App {
render () {
const { Component, pageProps, reduxStore } = this.props;
//reduxStore 通过高阶组件产生的store
return (
<Container>
<Provider store={reduxStore}>
<Component {...pageProps} />
</Provider>
</Container>
)
}
}
export default MyApp;
{
"presets": ["next/babel"],//next/babel next内置的模块,脚手架自带无需安装
"plugins": [
// @babel/plugin-proposal-decorators 这个插件,同时启用装饰器
["@babel/plugin-proposal-decorators", {
"legacy": true
}],
[
"import",
{
"libraryName": "antd",
"style": "css"//这个改成ture,就可以使用less编译了,同时也能更改主题
}
]
]
}
const withCss = require('@zeit/next-css')
const withSass = require('@zeit/next-sass');
// const withLess = require('@zeit/next-less');//需要使用less不实用scss的大佬,把withSass替换成withSass即可,如果都需要就都引入
const withTypescript = require('@zeit/next-typescript');//引入typescript,让next解析
const withPlugins = require("next-compose-plugins/lib");//结合sass css
module.exports = withPlugins([withSass,withCss,withTypescript],{
webpack: (config, { isServer }) => {
if (isServer) {
const antStyles = /antd\/.*?\/style\/css.*?/
const origExternals = [...config.externals]
config.externals = [
(context, request, callback) => {
if (request.match(antStyles)) return callback()
if (typeof origExternals[0] === 'function') {
origExternals[0](context, request, callback)
} else {
callback()
}
},
...(typeof origExternals[0] === 'function' ? [] : origExternals),
]
config.module.rules.unshift({
test: antStyles,
use: 'null-loader',
});
}
return config
},
})
{
"compilerOptions": {
"allowJs": true,
"experimentalDecorators":true,
"allowSyntheticDefaultImports": true,
"jsx": "preserve",
"lib": ["dom", "es2019"],
"module": "esnext",
"moduleResolution": "node",
"noEmit": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"preserveConstEnums": true,
"removeComments": false,
"skipLibCheck": true,
"sourceMap": true,
"strict": false,
"target": "esnext"
}
}
*最后,打一波小广告,如果需要使用useReducer整合的大佬可以看我的另外一个推文
useRedcuer,使用createContext, useContext, useReducer整合多个reducer案例
代码地址(github):点击这里跳转