react-scaffold
寫在前面
開始時(shí)間
2018-09-04
本日志僅為FireLeaf-React-Scaffold 2.x搭建過程.
node
下載最新版
部分技術(shù)選擇:
React
React-router
Redux
axios
webpack ^4.17.2
UI:
antd
less
規(guī)范:
eslint
stylelint
init
# 創(chuàng)建項(xiàng)目
mkdir project-name && cd project-name
# init
npm init
創(chuàng)建項(xiàng)目需要文件夾
# build-tools
mkdir build
# products-config
mkdir config
# script
mkdir script
# main-src
mkdir src
# static
mkdir static
webpack
# npm install --save-dev webpack@<version>
npm install --save-dev webpack
npm install --save-dev webpack-cli
# merge
npm install --save-dev webpack-merge
webpack 配置build/
webpack.base.config.js: 基礎(chǔ)配置
webpack.dev.config.js: dev模式配置
webpack.prod.config.js: prod模式配置
Babel
Babel 是一個(gè) JavaScript 編譯器, 進(jìn)行語法轉(zhuǎn)換扩劝,可按需加載插件浩销。
開始
npm i babel-loader@7 babel-core --save-dev
babel-loader: 這個(gè)包允許使用babel和webpack來轉(zhuǎn)換JavaScript文件肿男。
babel-core: 如果某些代碼需要調(diào)用Babel的API進(jìn)行轉(zhuǎn)碼戏自,就要使用babel-core模塊。
npm install babel-preset-env babel-preset-stage-0 babel-preset-react --save-dev
babel-preset-react 用于解析 JSX
babel-preset-stage-0 用于解析 ES7 提案
babel-preset-env: babel常用的轉(zhuǎn)義器:相當(dāng)于 es2015 ,es2016 ,es2017 及最新版本涮俄。
-
stage-x:
Stage 0 - 稻草人: 只是一個(gè)想法夫偶,可能是 babel 插件界睁。
Stage 1 - 提案: 初步嘗試。
Stage 2 - 初稿: 完成初步規(guī)范兵拢。
Stage 3 - 候選: 完成規(guī)范和瀏覽器初步實(shí)現(xiàn)翻斟。
Stage 4 - 完成: 將被添加到下一年度發(fā)布。
npm install babel-plugin-transform-runtime --save-dev
- babel-plugin-transform-runtime: 類babel-polyfill, 按需polyfill
.babelrc 配置
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"es2015",
"react",
"stage-0"
],
"plugins": [
"transform-runtime"
]
}
資源處理
img说铃、fonts访惜、media
npm i url-loader file-loader --save-dev
編譯css
npm install css-loader style-loader --save-dev
css-loader使你能夠使用類似@import 和 url(...)的方法實(shí)現(xiàn) require()的功能;
style-loader將所有的計(jì)算后的樣式加入頁(yè)面中腻扇; 二者組合在一起使你能夠把樣式表嵌入webpack打包后的JS文件中债热。
使用less
這里使用less, 其他預(yù)編譯樣式配置類似
npm i less less-loader --save-dev
樣式兼容
npm i autoprefixer postcss-loader --save-dev
配置 postcss.config.js
module.exports = {
plugins: [
require('autoprefixer')({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
],
flexbox: 'no-2009',
})
]
};
樣式文件拆分
npm install --save-dev mini-css-extract-plugin
webpack.base.config.js
配置
{
test: /\.css$/,
exclude: /node_modules/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader'
]
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'less-loader'
]
}
...
plugins: [
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: "[name].css",
chunkFilename: "[id].css"
})
]
webpack-config
webpack-dev
dev模式下webpack配置
npm i --save-dev html-webpack-plugin open-browser-webpack-plugin
webpack-prod
prod模式下webpack配置
npm i --save-dev optimize-css-assets-webpack-plugin
webpack-server
webpack 開發(fā)下的 server配置, 主要有下面兩種方式
webpack-dev-server
https://www.webpackjs.com/guides/development/#%E4%BD%BF%E7%94%A8-webpack-dev-server
npm i --save-dev webpack-dev-server
express + + webpack-dev-middleware
express 服務(wù)(node)+ webpack-dev-middleware + webpack-hot-middleware
npm i --save-dev webpack-dev-middleware webpack-hot-middleware eventsource-polyfill express
# server log
npm i --save-dev rimraf
webpack-build-prod
# 終端 spinner
npm i --save-dev ora rimraf chalk
webpack 其他配置
1、copy靜態(tài)資源 static
npm i --save-dev copy-webpack-plugin
2幼苛、壓縮打包文件
npm i --save-dev zip-webpack-plugin
規(guī)范
代碼窒篱、樣式編碼規(guī)范,代碼簡(jiǎn)潔易讀舶沿,提升項(xiàng)目開發(fā)效率墙杯。:blush:
http://eslint.cn/ : 代碼規(guī)范
https://stylelint.cn/ : 樣式規(guī)范
http://editorconfig.org/ : 編碼規(guī)范
npm install babel-eslint eslint-plugin-react eslint stylelint stylelint-config-standard --save-dev
vscode + eslint
自動(dòng)檢測(cè)排查,補(bǔ)全修復(fù)
"eslint.autoFixOnSave": true,
"eslint.validate": [
// "javascript",
// "javascriptreact",
// "html",
// "vue"
{
"language": "javascript",
"autoFix": true
},
{
"language": "javascriptreact",
"autoFix": true
},
{
"language": "vue",
"autoFix": true
},
{
"language": "jsx",
"autoFix": true
},
{
"language": "html",
"autoFix": true
}
],
stylelint autofix
樣式自動(dòng)修復(fù)
npm install stylelint-webpack-plugin --save-dev
webpack-config
new StyleLintPlugin({
// 正則匹配想要lint監(jiān)測(cè)的文件
files: ['src/**/*.l?(e|c)ss'],
cache: true,
fix: true
})
running
package.json 配置 script命令
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"lint": "eslint --ext .js src script config test && npm run lint:style",
"lint:fix": "eslint --fix --ext .js src script config test && npm run lint:style",
"lint-staged": "lint-staged",
"lint-staged:js": "eslint --ext .js",
"lint:style": "stylelint \"src/**/*.less\" --syntax less",
"start": "node script/server.js", // express server
"dev": "node script/dev-server.js", // webpack dev server
"build": "node script/prod.js" // webpack build prod
}
? 注意 寫進(jìn)package.json中不能帶有注釋
持續(xù)集成服務(wù) Travis CI
綁定 Github 上面的項(xiàng)目括荡,只要有新的代碼高镐,就會(huì)自動(dòng)抓取。然后畸冲,提供一個(gè)運(yùn)行環(huán)境嫉髓,執(zhí)行測(cè)試,完成構(gòu)建邑闲,還能部署到服務(wù)器算行。
React
集成React
npm i --save react react-dom prop-types
react-router
升級(jí) 4.x https://zhuanlan.zhihu.com/p/27433116
npm i --save react-router-dom
# 導(dǎo)入 history
npm i --save history
history跳轉(zhuǎn):
https://github.com/brickspert/blog/issues/3
redux
redux數(shù)據(jù)處理 https://cn.redux.js.org/
npm i --save redux react-redux
react-router-redux
這個(gè)包的正式版4.x不支持react-router v4。你需要用 alpha 版 的react-router-redux苫耸。在package.json 里加入react-router-redux~5.0.0或者用yarn:
yarn add react-router-redux@5.0.0
react應(yīng)用熱更新
https://github.com/gaearon/react-hot-loader/issues/511#issuecomment-288673129
npm i --save-dev react-hot-loader
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { Provider } from 'react-redux';
import { AppContainer } from 'react-hot-loader';
import thunk from 'redux-thunk';
// 引入路由配置模塊
import RouterList from './router/';
import { createStore, applyMiddleware } from 'redux';
import reducer from './reducer/';
// redux 注入操作
const middleware = [thunk];
const store = createStore(reducer, applyMiddleware(...middleware));
// console.log(store.getState());
const mountNode = document.getElementById('app'); // 設(shè)置要掛在的點(diǎn)
const hotRender = Component => render(
<AppContainer>
<Provider store={store}>
<Component />
</Provider>
</AppContainer>
, mountNode);
hotRender(RouterList);
if(process.env.NODE_ENV === 'development') {
if(module.hot) {
module.hot.accept('./router/', (err) => {
if (err) {
console.log(err);
}
const RouterList = require('./router/').default;
unmountComponentAtNode(mountNode);
hotRender(RouterList);
});
}
}
異步import, code split
異步導(dǎo)入組件纱意,代碼拆分
npm i --save-dev babel-plugin-syntax-dynamic-import
npm i --save react-loadable
配置使用
解決異步loadable引入導(dǎo)致react-hot-loader失效
{
"presets": [
"react"
],
"plugins": [
"syntax-dynamic-import"
]
}
import Loadable from 'react-loadable';
import Loading from './Loading';
const LoadableComponent = Loadable({
loader: () => import('./Dashboard'),
loading: Loading,
})
export default class LoadableDashboard extends React.Component {
render() {
return <LoadableComponent />;
}
}
Antd
Ant Design 的 React 實(shí)現(xiàn), 螞蟻UI組件庫(kù) ant-design
# install
npm i antd --save
# 按需加載
npm i babel-plugin-import --save-dev
babel-config:
["import", { "libraryName": "antd", "libraryDirectory": "es", "style": true }]
定制主題
config/theme.js:
/**
* antd theme config
*/
const defaultColor = '#4285f4';
module.exports = () => {
return {
'primary-color': defaultColor,
'link-color': defaultColor,
'border-radius-base': '3px',
'menu-collapsed-width': '70px',
};
};
// const fs = require('fs')
// const path = require('path')
// const lessToJs = require('less-vars-to-js')
// module.exports = () => {
// const themePath = path.join(__dirname, './src/utiles/style/theme.less')
// return lessToJs(fs.readFileSync(themePath, 'utf8'))
// }
package.json:
"theme": "./config/theme.js",
webpack.base.config.js:
// 獲取theme
const fs = require('fs');
const pkgPath = path.resolve(__dirname, './package.json');
const pkg = fs.existsSync(pkgPath) ? require(pkgPath) : {};
let theme = {};
if (pkg.theme && typeof pkg.theme === 'string') {
let cfgPath = pkg.theme;
if (cfgPath.charAt(0) === '.') {
cfgPath = path.resolve(__dirname, cfgPath);
}
const getThemeConfig = require(cfgPath);
theme = getThemeConfig();
} else if (pkg.theme && typeof pkg.theme === 'object') {
theme = pkg.theme;
}
...
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
{
loader: 'less-loader',
options: {
"sourceMap": true,
"modules": false,
"modifyVars": theme,
'javascriptEnabled': true
}
}
]
}