next.js使用
路由映射
as的功能,在Link
后面加上as可以改变url显示的方式,但是原有的功能不会改变,路径的可选修饰符,该修饰符将显示在浏览器URL栏中。
在router.push
也是可以使用as功能,在push第二个参数改变一下
nextjs实际上面没有改变文件结构,所以直接访问是as修改url后的路径是访问不到的.使用koa可以解决这个问题
const handle = app.getRequestHandler()
Router的钩子
路由的事件有6种,分别是
const events = [
'routerChangeStart',
'routerChangeComplete',
'routerChangeError',
'beforeHistoryChange',
'hashCahngeStart',
'hashChangeComplete'
]
function makeEvent(type){
return (...args)=>{
console.log(type,...args);
}
}
events.forEach(event=>{
Router.events.on(event,makeEvent(event))
})
hashChange
是带#的跳转
historyChange
是不带#的跳转
getInitialProps
这个方法是nextjs特有的方法,可以在页面中获取数据,仅有pages文件夹下的js文件才会调用这个方法.在服务端就会调用这个方法,不用等到浏览器(客户端)来渲染.但是客户端也偶尔会执行这段代码,当路由跳转到这个页面就是客服端执行.如果是直接打开这个页面就是服务端执行
const A= ({router,name})=><Link href="/test/test" as="test"><div><Button>123456</Button><Comp prop="132">123{router.query.id}+{name}</Comp></div></Link>
A.getInitialProps=()=>{
return { //return 的所有东西都会当作A组件的props属性供A组件使用
name:'joke'
}
}
nextjs自定义app _app.js
import App,{Container} from 'next/app'
import 'antd/dist/antd.css'
class myApp extends App{
static async getInitialProps({Component,ctx}){//每次页面切换都会调用这个方法
co
let pageProps;
if(Component.getInitialProps){
pageProps = await Component.getInitialProps(ctx)
}
return {
pageProps
}
}
render(){
const {Component,pageProps} = this.props;
console.log(Component);
return(
<Container>
<Component {...pageProps}></Component>
</Container>
)
}
}
export default myApp
自定义_app.js 也有getInitialProps方法,这个方法会传入其他组件作为参数.组件内部也有getInitialProps,这里自定义组件复原了如果组件如果有getInitialProps的方法的内容就把他返回,供组件使用props,不然组件接受不到props.默认的app组件也是这样去做的
nextjs自定义document
import Document,{ Html,Head,Main,NextScript } from 'next/document';
class MyDocument extends Document{
static async getInitialProps(ctx){ //要么不覆盖,覆盖的话就要把原来的props返回出去
const props = await Document.getInitialProps(ctx)
return {...props}
}
render(){
return (
<Html>
<Head>
<style>
{`.test {color:red}`}
</style>
</Head>
<body className="test">
<Main></Main>
<NextScript></NextScript>
</body>
</Html>
)
}
}
export default MyDocument
next定义样式
局部样式
<style jsx>{`//会自动隔离,在其他组件不生效
a{
color:blue
}
.link{
color:yellow
}
`}
</style>
全局样式
<style jsx global>{`//全局生效,不推荐,因为组件卸载之后样式也会消失,全局样式和局部样式可以共存,多加一个style就可以
a{
color:blue
}
.link{
color:yellow
}
`}
</style>
css-in-js
import Document, { Html, Head, Main, NextScript } from "next/document";
import { ServerStyleSheet } from "styled-components";
function withLog(Comp) {
return props => {
console.log(props);
return <Comp {...props} />;
};
}
class MyDocument extends Document {
static async getInitialProps(ctx) {
//要么不覆盖,覆盖的话就要把原来的props返回出去
const originalRenderPage = await ctx.renderPage;
const sheet = new ServerStyleSheet();
try {
ctx.renderPage = () => {
return originalRenderPage({
enhanceApp: App => props =>
sheet.collectStyles(<App {...props}></App>)
});
};
const props = await Document.getInitialProps(ctx);
return { ...props,style:<>{props.styles}{sheet.getStyleElement()}</> };
} finally {
sheet.seal();
}
}
render() {
return (
<Html>
<Head>
<style>{`.test {color:red}`}</style>
</Head>
<body className="test">
<Main></Main>
<NextScript></NextScript>
</body>
</Html>
);
}
}
export default MyDocument;
.babelrc中添加如下配置
react hooks
function组件通过hooks新增了保存state和更新state的能力,通过API 分别是useState和useEffect
useReduce,useReduce是更新复杂状态比较方便
useState,useEffect
import React, { useState, useEffect, useReducer } from "react";
function MyCountFunction() {
const [count, setCount] = useState(0);
const [name, setName] = useState("jojo");
// useEffect(() => {
// //组件渲染完成会触发这个回调
// const interval = setInterval(() => {
// // setCount(2); //这样是将count直接赋值为2
// setCount(c => c + 1); //这样是将count作为形参传入,更新count值为返回的结果
// }, 1000);
// return () => clearInterval(interval); //返回的函数会在组件卸载时候调用
// }, []);
useEffect(()=>{
console.log('effect invoked')
return ()=>console.log('effect deteched');
},[])//如果传入name就会在name每次变化时触发useEffect的两个函数,同理count也是一样
return (
<div>
<span> {count} </span>
<input type="text" value={name} onChange={e => setName(e.target.value)} />
<div>{name}</div>
</div>
);
}
function countReducer(state, action) {
switch (action.type) {
case "add":
return state + 1;
case "minus":
return state - 1;
default:
return state;
}
}
function ReduceCountComponent() {
const [count, dispatchCount] = useReducer(countReducer, 0);
useEffect(() => {
//组件渲染完成会触发这个回调
const interval = setInterval(() => {
dispatchCount({ type: "add" });
}, 1000);
return () => clearInterval(interval); //返回的函数会在组件卸载时候调用
}, []);
return <span> {count} </span>;
}
export default MyCountFunction;
useref
useRef使得函数组件也能实现只有component组件才能实现的API
usecontext
直接使用context就是值
useCallback
const handleSearchChange = useCallback((event)=>{
setSearch(event.target.value)
},[setSearch]) //由于setSearch不会改变 所以数组内不填也可以
useCallback
的作用在于利用 memoize
减少无效的 re-render
,来达到性能优化的作用。还是那句老生常谈的话,“不要过早的性能优化”。从实际开发的经验来看,在做这类性能优化时,一定得观察比较优化的结果,因为某个小角落的 callback
就可能导致优化前功尽弃,甚至是适得其反。
redux
import { createStore } from 'redux';
const initialState = {
count :0
}
const add = 'add'
function reducer(state = initialState,action){
console.log(state,action);
switch (action.type) {
case add:
return {count:state.count+1}
default:
return state
}
}
const store = createStore(reducer,initialState)
store.dispatch({type:'add'})
store.subsribe(()=>{//每次在数据变化时会调用这个函数,可以在react中更新状态重新渲染
console.log(store.getState());
})
console.log(store.getState());
export default store
reducer
应该返回对象而不是返回一个普通字面量,这样才能更新组件
conbineReducers时,initialState也要合并
import { createStore, combineReducers } from "redux";
const initialState = {
count: 0
};
const add = "add";
function reducer(state = initialState, action) {
console.log(state, action);
switch (action.type) {
case add:
return { count: state.count + 1 };
default:
return state;
}
}
function userReducer(state = { username: "zhangsan" }, action) {
switch (action.type) {
case "changeName":
return { username: action.name };
default:
return state;
}
}
const allReducer = combineReducers(reducer,userReducer)
const store = createStore(allReducer, {count:initialState,user:{username:'zhangsan'}});
store.dispatch({ type: "add" });
store.subsribe(() => {
//每次在数据变化时会调用这个函数,可以在react中更新状态重新渲染
console.log(store.getState());
});
console.log(store.getState());
export default store;
action
import { createStore, combineReducers,applyMiddleware } from "redux";
import ReduxThunk from 'redux-thunk'
const store = createStore(allReducer, {count:initialState,user:{username:'zhangsan'}},applyMiddleware(ReduxThunk));//applyMiddleware(ReduxThunk)使得action的dispatch内可以传入异步的请求数据
store.dispatch(异步数据);
import { createStore, combineReducers,applyMiddleware } from "redux";
import ReduxThunk from 'redux-thunk'
const initialState = {
count: 0
};
const add = "add";
function reducer(state = initialState, action) {
console.log(state, action);
switch (action.type) {
case add:
return { count: state.count + action.num || 1 };
default:
return state;
}
}
function userReducer(state = { username: "zhangsan" }, action) {
switch (action.type) {
case "changeName":
return { username: action.name };
default:
return state;
}
}
function addSync(num){
return (dispatch,getState)=>{ //如果是给store.dispatch调用会传入两个参数就是dispatch,getState
setTimeout(()=>{
dispatch({type:'add',num})
},1000)
}
}
const allReducer = combineReducers({counter:reducer,user:userReducer})//合并要传入一个对象
const store = createStore(allReducer, {counter:initialState,user:{username:'zhangsan'}},applyMiddleware(ReduxThunk));//applyMiddleware(ReduxThunk)使得action的dispatch内可以传入异步的请求数据,第二个参数是状态的对象,要和第一个对象的键对应
store.dispatch({ type: "add" });
store.dispatch({ username: "lisi",type:'changName' });
store.dispatch(addSync(5))
store.subscribe(() => {
//每次在数据变化时会调用这个函数,可以在react中更新状态重新渲染
console.log(store.getState());
});
console.log(store.getState());
export default store;
异步请求有可能出现一点警告如下
react-redux
redux原本是独立于react的,redux有连接react的一种方案
-
要用provider在_app里面
-
provider的作用是使provider下面包裹的元素能够访问provider里面提供的值也就是store
-
怎么使用provider
-
在index里面引入connect组件
-
在export default的地方connect()(组件名) connect()返回一个方法,再调用这个方法所以是两个括号
export default connect( function mapStateToProps(state){ return { //把一些state转化为props传给A组件,可以在A的props获取值 count:state.counter.count, username:state.user.username } },function mapDispatchToProps(dispatch){ return { add:(num)=>dispatch({type:'add',num}),//这里不推荐直接用字符串做type rename:(name)=>{dispatch({type:'changeName',name:name})} } })(withRouter(A)) ;
## react中引入redux
现在在chrome调试工具就能够看到redux选项卡
## nextjs中的HOC (HOC是接受组件作为参数并返回新的组件)
```javascript
export default (Comp)=>{ //接受组件返回组件,可以对特定参数进行处理,
return function TestHocComo({name,otherprops}){
const name = name + 123;
return <Comp {...otherprops} name={name}></Comp>
}
}
在redux中把props改变
export default (Comp)=>{
function TestHocComo({Component,pageProps,...rest}){ //配合_app来使用的话可以接收Component pageProps
// const name = name + 123;
console.log(Component,pageProps);
pageProps&&(pageProps.test=123)
return <Comp Component={Component} pageProps={pageProps} {...rest}></Comp>
}
TestHocComo.getInitialProps = Comp.getInitialProps
return TestHocComo
}
这样的好处是把redux内部的代码减少,减少了代码的耦合性,要用的时候只需要包裹一下组件,不必把组件内部代码写的很乱
redux集成到next
renderer
可以使一个组件拥有另一个组件的样式
layout.jsx
const Comp = ({ color, children, style }) => (
<div style={{ color, ...style }}>{children}</div>
);
<Contaniner renderer={<div className="header-inner" />}>
<div className="header-left">
<div className="logo">
{/* <Icon type="github" /> */}
<GithubOutlined style={IconStyle} />
</div>
<div>
<Input.Search
placeholder="搜索仓库"
value={search}
onChange={handleSearchChange}
onSearch={handleSearch}
/>
</div>
</div>
<div className="header-right">
<div className="user">
<Avatar size={40} icon={<UserOutlined />} />
</div>
</div>
</Contaniner>
<Content>
<Contaniner
// comp="p" //传入默认标签是p
renderer={<Comp color="red" />}
>
{children}
</Contaniner>
</Content>
Container.jsx
import {cloneElement} from 'react'
import { render } from 'react-dom'
const style = {
width:'100%',
maxWidth:800,
marginLeft:'auto',
marginRight:'auto',
paddingLeft:20,
paddingRight:20
}
export default ({
children,
// comp:Comp='div', //接收默认组件,解构赋值把形参改为大写
renderer
})=>{
return cloneElement(renderer,{
style:Object.assign({},renderer.props.style,style),
children
})
// return <Comp style={style}>{children}</Comp>
}
是renderer.props.style
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!