- 从零搭建React全家桶框架教程
- 创建日期:2019.3.12
- 修改日期:2019.3.15
- source
- 写在前面
- 说明
- init项目
- babel
- 路由启动是需要配置服务器的、
- 模块热替换
- redux
- 编译CSS
- 编译图片
- 按需加载
- 缓存
- 提取公共代码
- 生产环境构建
- 文件压缩
- 指定环境
- 优化缓存
- public path
- 打包优化
- 抽取css
- 使用axios和middleware优化API请求
- 调整文本编辑器,实现自动编译
- 合并提取webpack公共配置
- 优化目录结构并增加404页面
- 加入 babel-plugin-transform-runtime 和 babel-polyfill
- 集成PostCSS
- redux 模块热替换配置
- 模拟AJAX数据之Mock.js
- 使用 CSS Modules
- 使用 json-server 代替 Mock.js
- 快速生成Github README.md的目录
- Babel的使用
- Babel 入门教程
- npm scripts 使用指南
- 初探 React Router 4.0
- webpack-dev-server使用方法,看完还不会的来找我~
- Using webpack-dev-server
- Document React Hot Loader 3
- Redux 入门教程(一):基本用法
- Redux中文文档
- redux的数据流
- React 实践心得:react-redux 之 connect 方法详解
- 异步 Action
- Middleware
- devtool
- 按需加载
- babel-plugin-transform-runtime 和 babel-polyfill
- Mock.js
- CSS Modules 用法教程
交流加QQ群【572071150】备注【前端】
源码地址:https://github.com/brickspert/react-family 提问反馈:blog
因为本教程写于2017年9月,然而前端技术发展太快了。有些库的版本一直在升级,所以你如果碰到奇怪的问题,请先检查下安装的库版本是否和我源码中的一样。please~
大家阅读的时候,照着目录来阅读哦,有些章节不在文章里面。要点链接的~
当我第一次跟着项目做react项目的时候,由于半截加入的,对框架了解甚少,只能跟着别人的样板写。对整个框架没有一点了解。
做项目,总是要解决各种问题的,所以每个地方都需要去了解,但是对整个框架没有一个整体的了解,实在是不行。
期间,我也跟着别人的搭建框架的教程一步一步的走,但是经常因为自己太菜,走不下去。在经过各种蹂躏之后,对整个框架也有一个大概的了解,
我就想把他写下来,让后来的菜鸟能跟着我的教程对react全家桶有一个全面的认识。
我的这个教程,从新建根文件夹开始,到成型的框架,每个文件为什么要建立?建立了干什么?每个依赖都是干什么的?一步一步写下来,供大家学习。
当然,这个框架我以后会一直维护的,也希望大家能一起来完善这个框架,如果您有任何建议,欢迎在这里留言,欢迎fork源码react-family。
我基于该框架react-family又做了一个兼容IE8的版本,教程在这里react-family框架兼容IE8教程。
技术栈均是目前最新的。
-
react 15.6.1
-
react-dom 15.6.1(必须与react版本号对应,不然会出现下方报错)
Uncaught TypeError: Cannot read property 'hasOwnProperty' of undefined
-
react-router-dom 4.2.2
-
redux 3.7.2
-
webpack 3.5.5
-
react-hot-loader v3
-
react-redux 5.1.1(否则报错如下)
__WEBPACK_IMPORTED_MODULE_0_react___default.a.createContext is not a function
-
uglifyjs-webpack-plugin@1(否则报错如下)
TypeError: Cannot read property 'compilation' of undefined
-
clean-webpack-plugin@1(否则报错如下)
clean-webpack-plugin only accepts an options object.
-
css-loader@0
Module build failed: TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be a string
│ .babelrc #babel配置文件
│ package-lock.json
│ package.json
│ README.MD
│ webpack.config.js #webpack生产配置文件
│ webpack.dev.config.js #webpack开发配置文件
│
├─dist
├─public #公共资源文件
└─src #项目源码
│ index.html #index.html模板
│ index.js #入口文件
│
├─component #组建库
│ └─Hello
│ Hello.js
│
├─pages #页面目录
│ ├─Counter
│ │ Counter.js
│ │
│ ├─Home
│ │ Home.js
│ │
│ ├─Page1
│ │ │ Page1.css #页面样式
│ │ │ Page1.js
│ │ │
│ │ └─images #页面图片
│ │ brickpsert.jpg
│ │
│ └─UserInfo
│ UserInfo.js
│
├─redux
│ │ reducers.js
│ │ store.js
│ │
│ ├─actions
│ │ counter.js
│ │ userInfo.js
│ │
│ ├─middleware
│ │ promiseMiddleware.js
│ │
│ └─reducers
│ counter.js
│ userInfo.js
│
└─router #路由文件
Bundle.js
router.js-
创建文件夹并进入
mkdir react-family && cd react-family -
init npm
npm init按照提示填写项目基本信息
Babel 把用最新标准编写的 JavaScript 代码向下编译成可以在今天随处可用的版本。 这一过程叫做“源码到源码”编译, 也被称为转换编译。
通俗的说,就是我们可以用ES6, ES7等来编写代码,Babel会把他们统统转为ES5。
- babel-core 调用Babel的API进行转码
- babel-loader babel装载器,针对不同的打包工具开发的启动装置
- babel-preset-es2015 用于解析 ES6
- babel-preset-react 用于解析 JSX
- babel-preset-stage-0 用于解析 ES7 提案
Nginx,Apache,IIS等配置启动一个简单的的WEB服务器。- 使用
webpack-dev-server来配置启动WEB服务器。
既然用到了webpack-dev-server,我们就看看它的其他的配置项。
看了之后,发现有几个我们可以用的。
-
color(CLI only)
console中打印彩色日志 -
historyApiFallback 任意的
404响应都被替代为index.html。有什么用呢?你现在运行npm start,然后打开浏览器,访问http://localhost:8080,然后点击Page1到链接http://localhost:8080/page1, 然后刷新页面试试。是不是发现刷新后404了。为什么?dist文件夹里面并没有page1.html,当然会404了,所以我们需要配置historyApiFallback,让所有的404定位到index.html。 -
host 指定一个
host,默认是localhost。如果你希望服务器外部可以访问,指定如下:host: "0.0.0.0"。比如你用手机通过IP访问。 -
hot 启用
Webpack的模块热替换特性。关于热模块替换,我下一小节专门讲解一下。 -
port 配置要监听的端口。默认就是我们现在使用的
8080端口。 -
proxy 代理。比如在
localhost:3000上有后端服务的话,你可以这样启用代理:proxy: { "/api": "http://localhost:3000" }
- progress(CLI only) 将编译进度输出到控制台。
HRM配置其实有两种方式,一种CLI方式,一种Node.js API方式。我们用到的就是CLI方式,比较简单。
Node.js API方式,就是建一个server.js等等,网上大部分教程都是这种方式,这里不做讲解了。
我们看下webpack模块热替换教程。
我们接下来要这么修改
package.json` 增加 `--hot
"start": "webpack-dev-server --config webpack.dev.config.js --color --progress --hot"src/index.js 增加module.hot.accept(),如下。当模块更新的时候,通知index.js。
src/index.js
import React from 'react';
import ReactDom from 'react-dom';
import getRouter from './router/router';
if (module.hot) {
module.hot.accept();
}
ReactDom.render(
getRouter(), document.getElementById('app'));现在我们执行npm start,打开浏览器,修改Home.js,看是不是不刷新页面的情况下,内容更新了?惊不惊喜?意不意外?
做模块热替换,我们只改了几行代码,非常简单的。纸老虎一个~
现在我需要说明下我们命令行使用的--hot,可以通过配置webpack.dev.config.js来替换,
向文档上那样,修改下面三处。但我们还是用--hot吧。下面的方式我们知道一下就行,我们不用。同样的效果。
const webpack = require('webpack');
devServer: {
hot: true
}
plugins:[
new webpack.HotModuleReplacementPlugin()
]你以为模块热替换到这里就结束了?nonono~
上面的配置对react模块的支持不是很好哦。
例如下面的demo,当模块热替换的时候,state会重置,这不是我们想要的。
为了在react模块更新的同时,能保留state等页面中其他状态,我们需要引入react-hot-loader~
Q: 请问webpack-dev-server与react-hot-loader两者的热替换有什么区别?
A: 区别在于webpack-dev-server自己的--hot模式只能即时刷新页面,但状态保存不住。因为React有一些自己语法(JSX)是HotModuleReplacementPlugin搞不定的。
而react-hot-loader在--hot基础上做了额外的处理,来保证状态可以存下来。(来自segmentfault)
思:redux的目的是应用UI和数据逻辑彻底分离,将操作抽象为action,将逻辑抽象为reducer,由store沟通协调,来改变应用的state,state具体反映就是界面的变化。
使用指定的 React Redux 组件 来 魔法般的 让所有容器组件都可以访问 store,而不必显示地传递它。只需要在渲染根组件时使用即可。
为了让action创建函数除了返回action对象外,还可以返回函数,我们需要引用redux-thunk。
npm install --save redux-thunkredux提供了一个combineReducers函数来合并reducer,不用我们自己合并哦。写起来简单,但是意思和我们
自己写的combinReducers也是一样的。
css-loader使你能够使用类似@import 和 url(...)的方法实现 require()的功能;
style-loader将所有的计算后的样式加入页面中; 二者组合在一起使你能够把样式表嵌入webpack打包后的JS文件中。
npm install css-loader style-loader --save-devwebpack.dev.config.js rules增加
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}npm install --save-dev url-loader file-loaderwebpack.dev.config.js rules增加
{
test: /\.(png|jpg|gif)$/,
use: [{
loader: 'url-loader',
options: {
limit: 8192
}
}]
}options limit 8192意思是,小于等于8K的图片会被转成base64编码,直接插入HTML中,减少HTTP请求。
为什么要实现按需加载?
我们现在看到,打包完后,所有页面只生成了一个build.js,当我们首屏加载的时候,就会很慢。因为他也下载了别的页面的js了哦。
如果每个页面都打包了自己单独的JS,在进入自己页面的时候才加载对应的js,那首屏加载就会快很多哦。
在 react-router 2.0时代, 按需加载需要用到的最关键的一个函数,就是require.ensure(),它是按需加载能够实现的核心。
在4.0版本,官方放弃了这种处理按需加载的方式,选择了一个更加简洁的处理方式。
现在你运行发现名字变成home.js,这样的了。棒棒哒!
那么问题来了home是在哪里设置的?webpack怎么知道他叫home?
其实在这里我们定义了,router.js里面
import Home from 'bundle-loader?lazy&name=home!pages/Home/Home';看到没。这里有个name=home。嘿嘿。
想象一下这个场景~
我们网站上线了,用户第一次访问首页,下载了home.js,第二次访问又下载了home.js~
这肯定不行呀,所以我们一般都会做一个缓存,用户下载一次home.js后,第二次就不下载了。
有一天,我们更新了home.js,但是用户不知道呀,用户还是使用本地旧的home.js。出问题了~
怎么解决?每次代码更新后,打包生成的名字不一样。比如第一次叫home.a.js,第二次叫home.b.js。
文档看这里
想象一下,我们的主文件,原来的bundle.js里面是不是包含了react,redux,react-router等等
这些代码??这些代码基本上不会改变的。但是,他们合并在bundle.js里面,每次项目发布,重新请求bundle.js的时候,相当于重新请求了
react等这些公共库。浪费了~
我们把react这些不会改变的公共库提取出来,用户缓存下来。从此以后,用户再也不用下载这些库了,无论是否发布项目。嘻嘻。
webpack文档给了教程,看这里
但是你现在可能发现编译生成的文件app.[hash].js和vendor.[hash].js生成的hash一样的,这里是个问题,因为呀,你每次修改代码,都会导致vendor.[hash].js名字改变,那我们提取出来的意义也就没了。其实文档上写的很清楚,
output: {
path: path.join(__dirname, './dist'),
filename: '[name].[hash].js', //这里应该用chunkhash替换hash
chunkFilename: '[name].[chunkhash].js'
}但是无奈,如果用chunkhash,会报错。和webpack-dev-server --hot不兼容,具体看这里。
现在我们在配置开发版配置文件,就向webpack-dev-server妥协,因为我们要用他。问题先放这里,等会我们配置正式版webpack.config.js的时候要解决这个问题。
开发环境(development)和生产环境(production)的构建目标差异很大。在开发环境中,我们需要具有强大的、具有实时重新加载(live reloading)或热模块替换(hot module replacement)能力的 source map 和 localhost server。而在生产环境中,我们的目标则转向于关注更小的 bundle,更轻量的 source map,以及更优化的资源,以改善加载时间。由于要遵循逻辑分离,我们通常建议为每个环境编写彼此独立的 webpack 配置。
文档看这里
webpack使用UglifyJSPlugin来压缩生成的文件。
npm i --save-dev uglifyjs-webpack-plugin许多 library 将通过与 process.env.NODE_ENV 环境变量关联,以决定 library 中应该引用哪些内容。例如,当不处于生产环境中时,某些 library 为了使调试变得容易,可能会添加额外的日志记录(log)和测试(test)。其实,当使用 process.env.NODE_ENV === 'production' 时,一些 library 可能针对具体用户的环境进行代码优化,从而删除或添加一些重要代码。我们可以使用 webpack 内置的 DefinePlugin 为所有的依赖定义这个变量:
webpack.config.js
module.exports = {
plugins: [
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('production')
}
})
]
}npm run build后发现vendor.[hash].js又变小了。
刚才我们把[name].[hash].js变成[name].[chunkhash].js后,npm run build后,
发现app.xxx.js和vendor.xxx.js不一样了哦。
但是现在又有一个问题了。
你随便修改代码一处,例如Home.js,随便改变个字,你发现home.xxx.js名字变化的同时,
vendor.xxx.js名字也变了。这不行啊。这和没拆分不是一样一样了吗?我们本意是vendor.xxx.js
名字永久不变,一直缓存在用户本地的。~
官方文档推荐了一个插件HashedModuleIdsPlugin
plugins: [
new webpack.HashedModuleIdsPlugin()
]现在你打包,修改代码再试试,是不是名字不变啦?错了,现在打包,我发现名字还是变了,经过比对文档,我发现还要加一个runtime代码抽取,
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime'
})加上这句话就好了~为什么呢?看下解释。
注意,引入顺序在这里很重要。CommonsChunkPlugin 的 'vendor' 实例,必须在 'runtime' 实例之前引入。
想象一个场景,我们的静态文件放在了单独的静态服务器上去了,那我们打包的时候,如何让静态文件的链接定位到静态服务器呢?
看文档Public Path
webpack.config.js output 中增加一个publicPath,我们当前用/,相对于当前路径,如果你要改成别的url,就改这里就好了。
output: {
publicPath : '/'
}你现在打开dist,是不是发现好多好多文件,每次打包后的文件在这里混合了?我们希望每次打包前自动清理下dist文件。
npm install clean-webpack-plugin --save-dev
webpack.config.js
const CleanWebpackPlugin = require('clean-webpack-plugin');
plugins: [
new CleanWebpackPlugin(['dist'])
]现在npm run build试试,是不是之前的都清空了。当然我们之前的api文件夹也被清空了,不过没关系哦~本来就是测试用的。
目前我们的css是直接打包进js里面的,我们希望能单独生成css文件。
我们使用extract-text-webpack-plugin来实现。
npm install --save-dev extract-text-webpack-plugin先安装下axios
npm install --save axios然后在dispatch(getUserInfo())后,通过redux中间件来处理请求逻辑。
中间件的教程看这里
我们想想中间件的逻辑
- 请求前
dispatchREQUEST请求。 - 成功后
dispatchSUCCESS请求,如果定义了afterSuccess()函数,调用它。 - 失败后
dispatchFAIL请求。
npm start看看我们的网络请求是不是正常哦。
使用自动编译代码时,可能会在保存文件时遇到一些问题。某些编辑器具有“安全写入”功能,可能会影响重新编译。
要在一些常见的编辑器中禁用此功能,请查看以下列表:
- Sublime Text 3 - 在用户首选项(user preferences)中添加 atomic_save: "false"。
- IntelliJ - 在首选项(preferences)中使用搜索,查找到 "safe write" 并且禁用它。
- Vim - 在设置(settings)中增加 :set backupcopy=yes。
- WebStorm - 在 Preferences > Appearance & Behavior > System Settings 中取消选中 Use "safe write"。
想象一个场景,现在我想给webpack增加一个css modules依赖,你会发现,WTF?我即要修改webpack.dev.config.js,又要修改webpack.config.js~
这肯定不行啊。所以我们要把公共的配置文件提取出来。提取到webpack.common.config.js里面~
webpack.dev.config.js和webpack.config.js写自己的特殊的配置。
这里我们需要用到webpack-merge来合并公共配置和单独的配置。
这样说一下,应该看代码就能看懂了。下次公共配置直接就写在webpack.common.config.js里面啦。
这里偷偷说下,我修改了
CleanWebpackPlugin的参数,不让他每次构建都删除api文件夹了。要不每次都得复制进去。麻烦~
npm install --save-dev webpack-merge现在我们优化下目录结构,把router和nav分开,新建根组件App。
component改名为components,因为是复数。。。注意修改引用的地方哦。- 新建根组件
components/App/APP.js
import React, {Component} from 'react';
import Nav from 'components/Nav/Nav';
import getRouter from 'router/router';
export default class App extends Component {
render() {
return (
<div>
<Nav/>
{getRouter()}
</div>
)
}
}- 新建
components/Nav/Nav组件,把router/router.js里面的nav提出来。 - 新建
components/Loading/Loading组件,把router/router.js里面的Loading提出来。 - 入口文件
src/index.js修改
import React from 'react';
import ReactDom from 'react-dom';
import {AppContainer} from 'react-hot-loader';
import {Provider} from 'react-redux';
import store from './redux/store';
import {BrowserRouter as Router} from 'react-router-dom';
import App from 'components/App/App';
renderWithHotReload(App);
if (module.hot) {
module.hot.accept('components/App/App', () => {
const NextApp = require('components/App/App').default;
renderWithHotReload(NextApp);
});
}
function renderWithHotReload(RootElement) {
ReactDom.render(
<AppContainer>
<Provider store={store}>
<Router>
<RootElement/>
</Router>
</Provider>
</AppContainer>,
document.getElementById('app')
)
}- 新建
pages/NotFound/NotFound组件。 - 修改
router/router.js,增加404
import NotFound from 'bundle-loader?lazy&name=notFound!pages/NotFound/NotFound';
<Route component={createComponent(NotFound)}/>-
先来说说babel-plugin-transform-runtime
在转换 ES2015 语法为 ECMAScript 5 的语法时,babel 会需要一些辅助函数,例如 _extend。babel 默认会将这些辅助函数内联到每一个 js 文件里,这样文件多的时候,项目就会很大。
所以 babel 提供了 transform-runtime 来将这些辅助函数“搬”到一个单独的模块 babel-runtime 中,这样做能减小项目文件的大小。
npm install --save-dev babel-plugin-transform-runtime修改.babelrc配置文件,增加配置
.babelrc
"plugins": [
"transform-runtime"
]-
Q: 为什么要集成
babel-polyfill?A:
Babel默认只转换新的JavaScript句法(syntax),而不转换新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign)都不会转码。 举例来说,ES6在Array对象上新增了Array.from方法。Babel就不会转码这个方法。如果想让这个方法运行,必须使用babel-polyfill,为当前环境提供一个垫片。
网上很多人说,集成了
transform-runtime就不用babel-polyfill了,其实不然,看看官方怎么说的:NOTE: Instance methods such as "foobar".includes("foo") will not work since that would require modification of existing built-ins (Use babel-polyfill for that).
所以,我们还是需要
babel-polyfill哦。npm install --save-dev babel-polyfill修改webpack两个配置文件。
webpack.common.config.js
app: [
"babel-polyfill",
path.join(__dirname, 'src/index.js')
]webpack.dev.config.js
app: [
'babel-polyfill',
'react-hot-loader/patch',
path.join(__dirname, 'src/index.js')
]参考地址:
- http://www.ruanyifeng.com/blog/2016/01/babel.html
- lmk123/blog#45
- https://github.com/thejameskyle/babel-handbook/blob/master/translations/zh-Hans/README.md
Q: 这是啥?为什么要用它?
他有很多很多的插件,我们举几个例子~
Autoprefixer这个插件,可以自动给css属性加浏览器前缀。
/*编译前*/
.container{
display: flex;
}
/*编译后*/
.container{
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}postcss-cssnext 允许你使用未来的 CSS 特性(包括 autoprefixer)
当然,它有很多很多的插件可以用,你可以去官网详细了解。我们今天只用postcss-cssnext。(它包含了autoprefixer)
npm install --save-dev postcss-loader
npm install --save-dev postcss-cssnext修改webpack配置文件,增加postcss-loader
webpack.dev.config.js
rules: [{
test: /\.(css|scss)$/,
use: ["style-loader", "css-loader", "postcss-loader"]
}]webpack.config.js
rules: [{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: ["css-loader", "postcss-loader"]
})
}]根目录增加postcss配置文件。
touch postcss.config.js
postcss.config.js
module.exports = {
plugins: {
'postcss-cssnext': {}
}
};现在你运行代码,然后写个css,去浏览器审查元素,看看,属性是不是生成了浏览器前缀?
今天突然发现,当修改reducer代码的时候,页面会整个刷新,而不是局部刷新唉。
这不行,就去查了webpack文档,果然是要配置的。看这里
代码修改起来也简单,增加一段监听reducers变化,并替换的代码。
src/redux/store.js
if (module.hot) {
module.hot.accept("./reducers", () => {
const nextCombineReducers = require("./reducers").default;
store.replaceReducer(nextCombineReducers);
});
}哦了~
每个改进都是为了解决问题。
现在我在开发中碰到了问题,我先描述下问题:
我们现在做前后端完全分离的应用,前端写前端的,后端写后端的,他们通过API接口连接。
前端同学心理路程:"后端同学接口写的好慢,我都没法调试了。"
是不是有这个问题呢?一般我们怎么解决?
第一种:自己这边随便造点数据,等后端接口写好了之后,再小修改,再调试。
第二种:想想我们之前获得用户信息的dist/api/user.json,我们可以用这种方式来调试。
但是想象下,我们要模拟一个文章列表,就要手动写几十列。oh~no!
并且,后端接口一般都不带.json,到时候对接,是不是还得改代码?
好了,下面介绍下今天的主角Mock.js。
他会做一件事情:拦截AJAX请求,返回需要的数据!
我们写AJAX请求的时候,正常写,Mock.js会自动拦截的。
Mock.js提供各种随机生成数据。具体可以去官网看~
下面我们就在项目中集成咯:
-
npm install mockjs --save-dev -
新建mock文件夹
touch mock -
模拟一个我们之前用到的
/api/user接口cd mock touch mock.jsmock/mock.jsimport Mock from 'mockjs'; let Random = Mock.Random; Mock.mock('/api/user', { 'name': '@cname', 'intro': '@word(20)' });
上面代码的意思就是,拦截
/api/user,返回随机的一个中文名字,一个20个字母的字符串。我知道你看不懂,你去看看Mock.js文档就能看懂啦!
-
与我们的项目连接。到目前为止,刚才定义的接口和我们的项目还没有关系。
先来做,在
src/index.js里面增加一行代码:src/index.jsimport '../mock/mock';
-
现在我们删除
dist/api文件夹,然后修改之间的接口路径,把.json去掉。rm -rf dist/apisrc/redux/actions/userInfo.js/*promise: client => client.get(`/api/user.json`)*/ promise: client => client.get(`/api/user`)
现在我们运行
npm start,到获取用户信息界面,看每次获取用户信息都会变化呀?
到这里还没完,我们还要配置:只有在开发坏境下,才引入mock,在生产坏境,不引入。
跟着我做:
先给mock文件夹加个别名,这个我就不单独介绍了:
webpack.common.config.js
resolve: {
alias: {
...
mock: path.join(__dirname, 'mock')
}
}webpack.dev.config.js增加
const webpack = require('webpack');
plugins:[
new webpack.DefinePlugin({
MOCK: true
})
]然后修改src/index.js刚才加的那句话为下面这样
if (MOCK) {
require('mock/mock');
}这样,就只会在npm start 开发模式下,才会应用mock,如果你不想用,就把MOCK改成false就好了。
哦了,到这里就结束了~回头缕下:
我们定义了mock,在index.js引入。
mock的工作就是,拦截AJAX请求,返回模拟数据。
关于什么是CSS Modules,我这里不介绍。
可以去看阮一峰的文章CSS Modules 用法教程
修改以下几个地方:
-
webpack.dev.config.jsmodule: { rules: [{ test: /\.css$/, use: ["style-loader", "css-loader?modules&localIdentName=[local]-[hash:base64:5]", "postcss-loader"] }] }
-
webpack.config.jsmodule: { rules: [{ test: /\.css$/, use: ExtractTextPlugin.extract({ fallback: "style-loader", use: ["css-loader?modules&localIdentName=[local]-[hash:base64:5]", "postcss-loader"] }) }] }
-
src/pages/Page1/page1.css.box { border: 1px solid red; }
-
src/pages/Page1/Page1.jsimport React, {Component} from 'react'; import style from './Page1.css'; import image from './images/brickpsert.jpg'; export default class Page1 extends Component { render() { return ( <div className={style.box}> this is page1~ <img src={image}/> </div> ) } }
enjoy it!
json-server和Mock.js一样,都是用来模拟接口数据的。
json-server功能更强大,支持分页,排序,筛选等等,具体的可以去看文档。
我们用json-server代替之前的Mock.js。
-
删除
Mock.js相关代码。一共两处,
webpack.dev.config.js,src/index.js -
npm install --save-dev json-server -
写个demo,我们生成虚假数据还是用
mockjs。
mock/mock.js
let Mock = require('mockjs');
var Random = Mock.Random;
module.exports = function () {
var data = {};
data.user = {
'name': Random.cname(),
'intro': Random.word(20)
};
return data;
};- 设置启动脚本
package.json
"mock": "json-server mock/mock.js --watch --port 8090",
"mockdev": "npm run mock & npm start"- webpack.dev.config.js 增加个代理,把我们的API请求,代理到
json-server服务器去。
devServer: {
...
proxy: {
"/api/*": "http://localhost:8090/$1"
}
}哦了,你可以npm run mockdev启动项目,然后访问我们之前的用户信息接口,试试啦。
问题:windows不支持命令并行执行&,你可以分开执行,或者使用npm-run-all
熟悉Github的同学可能知道创建一个Repo,通常都会生成一个README.md。好的README能增加代码的可阅读性。另外通常也可以将README作为开发文档。而这个README本身是遵循Markdown语法的,但是Markdown本身并没有绝对标准,Github的渲染方式与一些常用博客渲染方式不相同,导致在使用时有些麻烦。这里推荐一个Github上的教程。
事实上大部分和普通Markdown还是类似的,但是目录的语法差别蛮大,刚好对于笔者而言,最近需要在Github上文档上建立目录来使用,但是又不想写GFM的语法。这个时候刚好搜索到了一些可以用的开源代码。这里简单介绍一个目前使用的方法。
事实上解决方案还蛮多的(Github大法好)。
当时还在百度上搜索了下,找到了这个方案。
后面发现了gh-md-toc这个神器。
但是这个东西在Mac和Linux很友好,windows似乎不那么友好。不过这里也给了windows的解决方案。
就是github-markdown-toc.go。
github-markdown-toc.go Github地址
如果你有GO语言(又是你)的编译环境,可以尝试自己编译,如果没有,可以直接下载编译好的二进制文件。
下载下来之后,发现没有后缀名无法识别,实际上这是个exe文件,所以只需要暴力地在后面加上.exe就可以开始愉快使用了。
首先将README.md文档复制到gh-md-toc.exe的根目录下。
接着按住shift键同时右击。
打开Powershell窗口后,直接键入。
./gh-md-toc.exe README.md接下来只需将这段话复制粘贴到README.md里面即可。
作者:G小调的Qing歌
链接:https://www.jianshu.com/p/302abe331dcb
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。