小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

初次搭建Next.js項(xiàng)目歷程(精)

 新進(jìn)小設(shè)計(jì) 2020-03-01

使用create-next-app初始化項(xiàng)目

create-next-app和react官方提供的create-react-app(CRA)非常像,next.js官方也自帶了create-next-app(CNA),所以直接安裝就好。

sudo yarn global add create-next-app

安裝好之后就可以創(chuàng)建項(xiàng)目了

create-next-app project-name

生成之后的目錄結(jié)構(gòu)如下

    |-- .gitignore
    |-- package.json
    |-- yarn.lock
    |-- components
    |   |-- nav.js
    |-- pages
    |   |-- index.js
    |-- public
        |-- favicon.ico

我們可以查看下package.json

package.json

{
  "name": "next-demo4",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start"
  },
  "dependencies": {
    "next": "9.1.4",
    "react": "16.12.0",
    "react-dom": "16.12.0"
  }
}

然后執(zhí)行yarn dev運(yùn)行起來(lái)試試

image.png

配置koa路由

安裝好了之后,我們可以設(shè)置成koa路由,首先添加koa 和 koa-router依賴包,然后在根目錄下添加server.js文件,如下:

yarn add koa koa-router

server.js

const Koa = require("koa");
const next = require("next");
const Router = require("koa-router");

const port = parseInt(process.env.PORT, 10) || 3003;
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();

app.prepare().then(() => {
  const server = new Koa();
  const router = new Router();

  router.get("/", async ctx => {
    await app.render(ctx.req, ctx.res, "/", ctx.query);
    ctx.respond = false;
  });

  router.all("*", async ctx => {
    await handle(ctx.req, ctx.res);
    ctx.respond = false;
  });

  server.use(async (ctx, next) => {
    ctx.res.statusCode = 200;
    await next();
  });

  server.use(router.routes());
  server.listen(port, () => {
    console.log(`> Ready on http://localhost:${port}`);
  });
});

修改package.json文件,添加如下代碼:

"scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
+   "koa": "node server.js -p 3003"
  },

我們現(xiàn)在執(zhí)行yarn koa試試,運(yùn)行成功!

通過(guò)next.js官方提供的example案例來(lái)配置redux和immutable.js

next.js官方案例提供了周邊框架的搭建實(shí)例,方便開(kāi)發(fā)人員配置相關(guān)框架和插件,如下所示:
image.png

首先我們先來(lái)安裝redux相關(guān)依賴包,如下

yarn add redux redux-actions redux-saga redux-logger

接著我們找一下我們要搭建的redux和immutable.js相關(guān)實(shí)例:with-redux-saga和with-immutable-redux-wrapper,然后自己一點(diǎn)點(diǎn)的去配置,當(dāng)然中間肯定遇到了各種各樣的問(wèn)題,這里就不詳細(xì)說(shuō)明了,只展示配置好運(yùn)行起來(lái)沒(méi)問(wèn)題的代碼分享給大家,如下所示:

_app.js

import React from "react";
import { Provider } from "react-redux";
import App, { Container } from "next/app";
import withRedux from "next-redux-wrapper";
import withReduxSaga from "next-redux-saga";
import { fromJS } from "immutable";
import configureStore from "../service/redux/store";
import "../assets/styles/index.css";
import "antd/dist/antd.css";

@withRedux(configureStore)
@withReduxSaga
class NextApp extends App {
  static async getInitialProps({ Component, ctx }) {
    let pageProps = {};
    if (Component.getInitialProps) {
      pageProps = await Component.getInitialProps({ ctx });
    }
    return { pageProps };
  }

  render() {
    const { Component, pageProps, store } = this.props;
    return (
      <Container>
        <Provider store={store}>
          <Component {...pageProps} />
        </Provider>
      </Container>
    );
  }
}

export default NextApp;

store.js

import { createStore, compose, applyMiddleware } from "redux";
import createSagaMiddleware from "redux-saga";
import logger from "redux-logger";
import rootReducer from "./reducers";
import rootSaga from "./sagas";
import { fromJS } from "immutable";

// create the saga middleware
const sagaMiddleware = createSagaMiddleware();

// 組合middleware
const middleWares = [sagaMiddleware, logger];

export default function configureStore(preloadedState = fromJS({})) {
  const store = createStore(rootReducer, applyMiddleware(...middleWares));

  store.sagaTask = sagaMiddleware.run(rootSaga);
  return store;
}

reducers.js

import { combineReducers } from "redux";
import { autoCombineReducer } from "../../utils/autoCombineRedux";

export default combineReducers(autoCombineReducer());

sagas.js

import { all, fork } from "redux-saga/effects";
import { autoCombineSaga } from "../../utils/autoCombineRedux";

/*添加對(duì)action的監(jiān)聽(tīng) */
export default function* rootSaga() {
  yield all(autoCombineSaga());
}

這里我說(shuō)明下reducers.js和sagas.js用到的方法:autoCombineReducer和autoCombineSaga,以前我們寫reducer.js或者saga.js是把相關(guān)reducer或者saga文件import進(jìn)來(lái),然后注入到combineReducers()方法或者yield all()里面,好處是直觀,壞處就是每次寫一個(gè)reducer或者saga文件就要在這里加一下,是不是覺(jué)得非常麻煩,索性自己寫一個(gè)自動(dòng)注冊(cè)進(jìn)combineReducers()或者yield all()的方法,這個(gè)方法利用webpack的CONTEXT(require.context)把文件夾下名稱為reducer.js或者saga.js文件自動(dòng)加載進(jìn)來(lái),詳細(xì)請(qǐng)看下面代碼:

autoCombineRedux.js

import { all, fork } from "redux-saga/effects";

// 查詢所有文件夾下的所有文件名,以文件名數(shù)組形式返回
const getContext = (path, type) => {
  let CONTEXT = type == "reducer" ? require.context(".", true, /reducer\.js$/) : require.context(".", true, /saga\.js$/);
  if (path == "../") {
    CONTEXT = type == "reducer" ? require.context("../", true, /reducer\.js$/) : require.context("../", true, /saga\.js$/);
  }
  if (path == "../../") {
    CONTEXT = type == "reducer" ? require.context("../../", true, /reducer\.js$/) : require.context("../../", true, /saga\.js$/);
  }
  return CONTEXT;
};

export const autoCombineReducer = (path = "../") => {
  let CONTEXT = getContext(path, "reducer");

  // 獲取完組合成reducer對(duì)象
  const importReducer = req => (obj, path) => {
    //從路徑中獲取reducer name
    // output:[0: "demo",groups: undefined,index: 7,input: "./demo/reducer.js"]
    const [componentName] = path.match(/\w+(?=\/reducer\.js$)/);

    // 組合reducer對(duì)象
    const reducer = {
      [`${componentName}Reducer`]: req(path).default
    };

    return Object.assign({}, obj, reducer);
  };

  // 從CONTEXT路徑數(shù)組中組合成reducer對(duì)象返回
  const getReducers = ctx => ctx.keys().reduce(importReducer(ctx), {});
  return getReducers(CONTEXT);
};

export const autoCombineSaga = (path = "../") => {
  let CONTEXT = getContext(path, "saga");
  // 獲取完組合成reducer對(duì)象
  const forkSaga = req => path => {
    //從路徑中獲取reducer name
    // output:[0: "demo",groups: undefined,index: 7,input: "./demo/demo.reducer.js"]
    const [componentName] = path.match(/\w+(?=\/saga\.js$)/);
    return fork(req(path).default);
  };

  // 從CONTEXT路徑數(shù)組中組合成reducer對(duì)象返回
  const getSagas = ctx => ctx.keys().map(forkSaga(ctx));
  return getSagas(CONTEXT);
};

大家有沒(méi)有發(fā)現(xiàn)其實(shí)上面已經(jīng)配置好了immutable.js,所以我們就可以直接在reduer文件里面使用immutable對(duì)象了,如下所示:

reducer.js

import { handleActions } from "redux-actions";
import { authTypes } from "./action";
import moment from "moment";
import { Map, fromJS, merge } from "immutable";

const initState = fromJS({
  user: null,
  token: ""
});

const authReducer = handleActions(
  {
    [authTypes.AUTH_SUCCESS]: (state, action) => {
      return state.merge({
        user: action.data.user,
        token: action.data.token
      });
    },
    [authTypes.SIGN_OUT]: (state, action) => {
      return state.merge({
        user: null,
        token: ""
      });
    }
  },
  initState
);

export default authReducer;

這樣就完成了redux和immutable.js的配置了,下面我們來(lái)配置antd。

使用next-plugins來(lái)配置antd和hiynn-design

ant-design
antd 就不需要我多說(shuō)明了吧,國(guó)內(nèi)頂級(jí)大廠的杰出之作。

hiynn-design
hiynn-design是我公司(海云)前(我)端(創(chuàng))團(tuán)(建)隊(duì)(的)創(chuàng)建的UI庫(kù),分為標(biāo)準(zhǔn)化組件和可視化組件。

同樣的我們?cè)趎ext.js的example里面找到:with-ant-design-less 我們就100%參考這個(gè)實(shí)例來(lái)配置antd,首先我們來(lái)添加下相關(guān)依賴包:

yarn add @zeit/next-css @zeit/next-sass @zeit/next-less next-compose-plugins less-vars-to-js next-images 

yarn add babel-plugin-import @babel/plugin-proposal-decorators @babel/plugin-proposal-do-s @babel/plugin-proposal-class-properties

回過(guò)頭來(lái)我們?cè)賮?lái)看看next-plugins包含那些插件,如下所示:

image.png

這里就不一一詳細(xì)說(shuō)明了,我們只舉幾個(gè)重要的安裝包來(lái)說(shuō)明下。

next-images
在SPA項(xiàng)目里面我們使用import或者require來(lái)加載圖片,但是next.js不是那么簡(jiǎn)單的,它會(huì)提供你一個(gè)public文件夾來(lái)放靜態(tài)圖片,然后寫上絕對(duì)地址,這樣一來(lái)動(dòng)態(tài)添加圖片就變得麻煩,因此就有next-images這個(gè)依賴包的出現(xiàn)了,這個(gè)包的作用是讓你像import require那樣引入你的圖片。

next-compose-plugins
當(dāng)我們的next.config.js文件需要同時(shí)配置多個(gè)包的時(shí)候就會(huì)讓每個(gè)單獨(dú)配置的包分散開(kāi)了,這樣不利于維護(hù),因此就出現(xiàn)了next-compose-plugins把各個(gè)分散的包集合在一起,具體的請(qǐng)查看next-compose-plugins

接著我們就按照with-ant-design-less來(lái)配置.babelrc和next.config.js文件,這里我們同時(shí)支持了ant-design和hiynn-design,詳細(xì)代碼如下:

.babelrc

{
  "presets": [
    "@babel/preset-react", "next/babel"
  ],
  "plugins": [
    "@babel/plugin-proposal-do-s",
    [
      "@babel/plugin-proposal-decorators",
      {
        "legacy": true
      }
    ],
    ["@babel/plugin-proposal-class-properties", {
      "loose": true
    }],
    //按需加載antd樣式
    [
      "import", {
        "libraryName": "antd",
        "style": true
      },
      "antd"
    ],
    //按需加載hiynnd樣式
    [
      "import",
      {
        "libraryName": "hiynn-design",
        "style": true
      },
      "hiynn-design"
    ]
  ]
}

next.config.js

const withCss = require("@zeit/next-css");
const withSass = require("@zeit/next-sass");
const withPlugins = require("next-compose-plugins");
const withLess = require("@zeit/next-less");
const lessToJS = require("less-vars-to-js");
const fs = require("fs");
const path = require("path");
const withImages = require("next-images");

// Where your antd-custom.less file lives
const themeVariables = lessToJS(fs.readFileSync(path.resolve(__dirname, "./src/assets/styles/antd-custom.less"), "utf8"));

module.exports = withPlugins([withCss, withSass, withLess, withImages], {
  cssLoaderOptions: {
    importLoaders: 1
  },
  lessLoaderOptions: {
    javascriptEnabled: true,
    modifyVars: themeVariables // make your antd custom effective
  },
  webpack: (config, { isServer }) => {
    if (isServer) {
      const antStyles = /antd\/.*?\/style.*?/;
      const hiynnStyles = /hiynn-design\/.*?\/style.*?/;
      const origExternals = [...config.externals];
      config.externals = [
        (context, request, callback) => {
          if (request.match(antStyles) || request.match(hiynnStyles)) {
            return callback();
          }
          if (typeof origExternals[0] === "function") {
            origExternals[0](context, request, callback);
          } else {
            callback();
          }
        },
        ...(typeof origExternals[0] === "function" ? [] : origExternals)
      ];
      config.module.rules.push(
        {
          test: antStyles,
          use: "null-loader"
        },
        {
          test: hiynnStyles,
          use: "null-loader"
        }
      );
    }
    return config;
  }
});

請(qǐng)注意,Next.js無(wú)法使用css-loader。 請(qǐng)參閱官方頁(yè)面上的警告
警告:不建議添加加載程序以支持新的文件類型(css,less,svg等),因?yàn)橹挥锌蛻舳舜a通過(guò)webpack捆綁在一起,因此在初始服務(wù)器渲染中不起作用。 Babel插件是一個(gè)不錯(cuò)的選擇,因?yàn)樗鼈冊(cè)诜?wù)器/客戶端渲染之間一致地應(yīng)用

這也算是配置next.js中的一個(gè)小插曲吧,大家知道就好,因?yàn)槲覀兩厦嬉呀?jīng)解決了這個(gè)問(wèn)題。

Bug:Module parse failed: Unexpected character '@'

image

然后我們運(yùn)行起來(lái)試試,報(bào)了一個(gè)bug!我們明明已經(jīng)按照with-ant-design-less配置好了同時(shí)支持ant-design和hiynn-design為什么還報(bào)錯(cuò)?

接著我就找問(wèn)題原因,在segmentfault和next-plugins 的issues 上面提問(wèn),最后發(fā)現(xiàn)next.js不支持.pcss,因此我就把hiynn-design的庫(kù)從.pcss全部變成了.scss,這樣重新編譯并打包發(fā)布并安裝,運(yùn)行起來(lái)就沒(méi)這個(gè)錯(cuò)了,但是又有個(gè)新的bug,真的是一波未平一波又起?。?/p>

Bug:SyntaxError:Unexpected string

image

接著我就又找問(wèn)題原因,同樣我又在segmentfault和next-plugins的issues上面提問(wèn),最后發(fā)現(xiàn)是因?yàn)槲覂蓚€(gè)庫(kù)的.babelrc文件里面按需加載的libraryDirecory不一致導(dǎo)致的,hiynn-design庫(kù)使用的libraryDirecory是'es',而next.js項(xiàng)目使用的libraryDirecory是'lib',然后我統(tǒng)一了一下兩個(gè)libraryDirecory就沒(méi)問(wèn)題了,代碼如下:

[
  "import", {
    "libraryName": "antd",
    "style": true
  },
  "antd"
],

Bug:SyntaxError:Invalid or unexpected token

image.png
這個(gè)問(wèn)題是因?yàn)閍ntd .less文件后綴引起的,在next.config.js加上下面這段代碼就OK了

next.config.js

const withCss = require("@zeit/next-css");
const withSass = require("@zeit/next-sass");
const withPlugins = require("next-compose-plugins");
const withLess = require("@zeit/next-less");
const lessToJS = require("less-vars-to-js");
const fs = require("fs");
const path = require("path");
const withImages = require("next-images");

// Where your antd-custom.less file lives
const themeVariables = lessToJS(fs.readFileSync(path.resolve(__dirname, "./src/assets/styles/antd-custom.less"), "utf8"));
+ require.extensions[".less"] = () => {};
module.exports = withPlugins([withCss, withSass, withLess, withImages], {
  cssLoaderOptions: {
    importLoaders: 1
  },
  lessLoaderOptions: {
    javascriptEnabled: true,
    modifyVars: themeVariables // make your antd custom effective
  },
  webpack: (config, { isServer }) => {
    if (isServer) {
      const antStyles = /antd\/.*?\/style.*?/;
      const hiynnStyles = /hiynn-design\/.*?\/style.*?/;
      const origExternals = [...config.externals];
      config.externals = [
        (context, request, callback) => {
          if (request.match(antStyles) || request.match(hiynnStyles)) {
            return callback();
          }
          if (typeof origExternals[0] === "function") {
            origExternals[0](context, request, callback);
          } else {
            callback();
          }
        },
        ...(typeof origExternals[0] === "function" ? [] : origExternals)
      ];
      config.module.rules.push(
        {
          test: antStyles,
          use: "null-loader"
        },
        {
          test: hiynnStyles,
          use: "null-loader"
        }
      );
    }
    return config;
  }
});

最后這些問(wèn)題解決后我們?cè)龠\(yùn)行就不再報(bào)錯(cuò)了,運(yùn)行正常!雖然我寫這篇文章只用了2個(gè)小時(shí),但是解決這些問(wèn)題我足足花了一個(gè)多月。

總結(jié)

1、next.js對(duì)css-loader支持不友好,應(yīng)該是所有node.js基于后臺(tái)的通病了
2、配置redux和immutable網(wǎng)上有現(xiàn)成的案例可以根據(jù)這些案例進(jìn)行配置
3、配置antd和hiynnd是最辛苦的,雖然最后成功了
4、postcss一般做為輔助sass或者less庫(kù)使用最好,而不要單獨(dú)使用,比如我的hiynn-design庫(kù)一開(kāi)始使用的全是.pcss結(jié)尾,然后就變成.scss。

參考

less、sass和postcss總結(jié)
next-plugins
ModuleParseError: Module parse failed: Unexpected character '@'
SyntaxError: Unexpected string
nextjs-starter-kit
nextjs-starter
嗶哩嗶哩(B站)的前端之路
PostCSS深入學(xué)習(xí): PostCSS和Sass、Stylus或LESS一起使用
Next.js 使用指南2-路由與加載
next-images
next.js 采坑錄(服務(wù)端渲染)

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多