react/react-router/redux/axios/ material-ui
react/react-router/redux/axios/ material-ui
React 项目样板
共160Star
详细介绍
react-douban
react/react-router/redux/axios/ material-ui
登陆注册功能
使用了策略模式进行表单验证,避免了大量的if else 语句
// 策略类
// 策略类(验证规则)
const formStrategy = {
/**
* 不为空
* @param {String} val 内容
* @param {String} err 错误信息
*/
notNull (val, err) {
let newVal = val.replace(/(^\s+)|(\s+$)/g, "")
if (newVal === '') {
return err
}
},
/**
* 密码验证
* @param {Stirng} val 内容
* @param {String} err 错误信息
*/
password (val, err) {
if (!/^[a-zA-Z]\w{5,17}$/.test(val)) {
return err
}
},
/**
* 密码不能重复
* @param {Array} val 前一次输入的密码以及后一次输入的密码
* @param {String} err 错误信息
*/
passwordNotRepeat (val, err) {
if (val[0] !== val[1] || val[0] === '' || val[1] === '') {
return err
}
}
}
export default formStrategy
// 验证类
import formStrategy from './strategy'
// 验证类
class Validation {
constructor () {
// 验证规则的集合
this.rule = []
}
/**
* 添加验证规则
* @param {String} value 验证的内容
* @param {Array} rule 验证内容的规则
*/
addRule (value, rule) {
rule.forEach(ruleItem => {
this.rule.push(() => {
let { strategy, errMsg } = ruleItem
return formStrategy[strategy](value, errMsg)
})
})
}
/**
* 开始验证
*/
startValidation () {
for (let i = 0; i < this.rule.length; i++) {
let msg = this.rule[i]()
if (msg) {
return msg
}
}
}
}
export default Validation
bind是使用apply返回的新的匿名函数,注意使用addEventlistener时候,匿名函数是无法解绑的
this.scroll = this.onScroll.bind(this)
/**
* window添加scroll事件
*/
addEventScroll () {
window.addEventListener('scroll', this.scroll)
}
/**
* window删除scroll事件
*/
removeEventScroll () {
window.removeEventListener('scroll', this.scroll)
}
create-react-app 中使用字体图标
// 注意字体文件的路径,不能直接使用./../xxx 需要 ../../xxx,具体原因我不太明白
// stack中找到了类似的问题。链接如下
https://stackoverflow.com/questions/41408976/how-to-use-local-webfont-in-imported-sass-file
// 修改webpack的配置文件
{
test: [/\.woff2$/, /\.eot$/, /\.ttf$/, /\.otf$/],
loader: require.resolve('url-loader'),
options: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]'
}
},
// 修改iconfont.css
// 记得使用全局:global()全局样式
@font-face {font-family: "iconfont";
src: url('../icon/iconfont.eot?t=1519034602999'); /* IE9*/
src: url('../icon/iconfont.eot?t=1519034602999#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff;charset=utf-8;base64,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') format('woff'),
url('../icon/iconfont.ttf?t=1519034602999') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
url('../icon/iconfont.svg?t=1519034602999#iconfont') format('svg'); /* iOS 4.1- */
}
:global(.iconfont) {
font-family:"iconfont" !important;
font-size:16px;
font-style:normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
:global(.icon-cuowu:before) { content: "\e641"; }
:global(.icon-jushoucang:before) { content: "\e643"; }
:global(.icon-sousuo:before) { content: "\e651"; }
:global(.icon-xiangshangjiantou:before) { content: "\e65d"; }
:global(.icon-xiangxiajiantou:before) { content: "\e65e"; }
:global(.icon-xiangyoujiantou:before) { content: "\e65f"; }
:global(.icon-xiangzuojiantou:before) { content: "\e660"; }
css 模块化
// webpack.config.dev.js
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
modules: true
},
},
// webpack.config.prod.js
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
minimize: true,
sourceMap: shouldUseSourceMap,
modules: true
},
},
less的添加
// webpack.config.dev.js
{
test: /\.(css|less)$/,
use: [
require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
modules: true
},
},
{
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebookincubator/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
],
flexbox: 'no-2009',
}),
],
},
},
{
loader: require.resolve('less-loader')
}
],
},
// webpack.config.prod.js
{
test: /\.(css|less)$/,
loader: ExtractTextPlugin.extract(
Object.assign(
{
fallback: {
loader: require.resolve('style-loader'),
options: {
hmr: false,
},
},
use: [
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
minimize: true,
sourceMap: shouldUseSourceMap,
modules: true
},
},
{
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebookincubator/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
],
flexbox: 'no-2009',
}),
],
},
},
{
loader: require.resolve('less-loader')
}
],
},
extractTextPluginOptions
)
),
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
},
react-router 异步加载组件
import React, { Component } from 'react'
function AsyncComponent (importComponent) {
return class AsyncRouterComponent extends Component {
constructor (props) {
super(props)
this.state = {
component: null
}
}
async componentDidMount () {
const { default: component } = await importComponent()
this.setState({
component: component
})
}
render () {
const component = { this.state }
return (
component ? <component {...this.props}/> : null
)
}
}
}
export default AsyncComponent
Throttle
function Throttle (fn, delay = 500) {
let self = fn
let timer = null
let isFirst = false
return function (...rest) {
if (!isFirst) {
self(...rest)
}
if (timer) {
return
}
timer = setTimeout(() => {
clearTimeout(timer)
timer = null
self(...rest)
}, delay)
}
}
export default Throttle
自定义Link
import React, { Component } from 'react'
import { Link, Route } from 'react-router-dom'
import style from './CustomLink.css'
class CustomLink extends Component {
render () {
let { to, children, ...rest } = this.props
return (
<Route path={to} children={({ match }) => {
return (
<div>
<Link
className={`${style['link']} ${match ? style['active-link'] : ''}`}
to={to}
{...rest}>
{ children }
</Link>
</div>
)
}} />
)
}
}
export default CustomLink
-
87 Star
-
5 Star